@promptbook/node 0.89.0-9 โ†’ 0.92.0-10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +9 -7
  2. package/esm/index.es.js +320 -97
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/servers.d.ts +40 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +14 -4
  6. package/esm/typings/src/_packages/deepseek.index.d.ts +2 -0
  7. package/esm/typings/src/_packages/google.index.d.ts +2 -0
  8. package/esm/typings/src/_packages/types.index.d.ts +18 -0
  9. package/esm/typings/src/_packages/utils.index.d.ts +6 -0
  10. package/esm/typings/src/cli/cli-commands/login.d.ts +0 -1
  11. package/esm/typings/src/cli/common/$provideLlmToolsForCli.d.ts +16 -3
  12. package/esm/typings/src/cli/test/ptbk.d.ts +1 -1
  13. package/esm/typings/src/commands/EXPECT/expectCommandParser.d.ts +2 -0
  14. package/esm/typings/src/config.d.ts +10 -19
  15. package/esm/typings/src/conversion/archive/loadArchive.d.ts +2 -2
  16. package/esm/typings/src/errors/0-index.d.ts +7 -4
  17. package/esm/typings/src/errors/PipelineExecutionError.d.ts +1 -1
  18. package/esm/typings/src/errors/WrappedError.d.ts +10 -0
  19. package/esm/typings/src/errors/assertsError.d.ts +11 -0
  20. package/esm/typings/src/execution/CommonToolsOptions.d.ts +4 -0
  21. package/esm/typings/src/execution/PromptbookFetch.d.ts +1 -1
  22. package/esm/typings/src/execution/createPipelineExecutor/getKnowledgeForTask.d.ts +12 -0
  23. package/esm/typings/src/execution/createPipelineExecutor/getReservedParametersForTask.d.ts +5 -0
  24. package/esm/typings/src/formats/csv/utils/csvParse.d.ts +12 -0
  25. package/esm/typings/src/formats/csv/utils/isValidCsvString.d.ts +9 -0
  26. package/esm/typings/src/formats/csv/utils/isValidCsvString.test.d.ts +1 -0
  27. package/esm/typings/src/formats/json/utils/isValidJsonString.d.ts +3 -0
  28. package/esm/typings/src/formats/json/utils/jsonParse.d.ts +11 -0
  29. package/esm/typings/src/formats/xml/utils/isValidXmlString.d.ts +9 -0
  30. package/esm/typings/src/formats/xml/utils/isValidXmlString.test.d.ts +1 -0
  31. package/esm/typings/src/llm-providers/_common/filterModels.d.ts +15 -0
  32. package/esm/typings/src/llm-providers/_common/register/{$provideEnvFilepath.d.ts โ†’ $provideEnvFilename.d.ts} +2 -2
  33. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +1 -1
  34. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +1 -1
  35. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizzardOrCli.d.ts +11 -2
  36. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -1
  37. package/esm/typings/src/llm-providers/_common/register/LlmToolsMetadata.d.ts +43 -0
  38. package/esm/typings/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +4 -0
  39. package/esm/typings/src/llm-providers/deepseek/deepseek-models.d.ts +23 -0
  40. package/esm/typings/src/llm-providers/google/google-models.d.ts +23 -0
  41. package/esm/typings/src/llm-providers/openai/OpenAiExecutionTools.d.ts +4 -0
  42. package/esm/typings/src/personas/preparePersona.d.ts +1 -1
  43. package/esm/typings/src/pipeline/PipelineJson/PersonaJson.d.ts +4 -2
  44. package/esm/typings/src/remote-server/openapi-types.d.ts +626 -0
  45. package/esm/typings/src/remote-server/openapi.d.ts +581 -0
  46. package/esm/typings/src/remote-server/socket-types/_subtypes/Identification.d.ts +7 -1
  47. package/esm/typings/src/remote-server/socket-types/_subtypes/identificationToPromptbookToken.d.ts +11 -0
  48. package/esm/typings/src/remote-server/socket-types/_subtypes/promptbookTokenToIdentification.d.ts +10 -0
  49. package/esm/typings/src/remote-server/startRemoteServer.d.ts +1 -2
  50. package/esm/typings/src/remote-server/types/RemoteServerOptions.d.ts +15 -9
  51. package/esm/typings/src/storage/env-storage/$EnvStorage.d.ts +40 -0
  52. package/esm/typings/src/types/typeAliases.d.ts +26 -0
  53. package/package.json +9 -5
  54. package/umd/index.umd.js +320 -97
  55. package/umd/index.umd.js.map +1 -1
  56. package/esm/typings/src/cli/test/ptbk2.d.ts +0 -5
package/esm/index.es.js CHANGED
@@ -30,7 +30,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
30
30
  * @generated
31
31
  * @see https://github.com/webgptorg/promptbook
32
32
  */
33
- const PROMPTBOOK_ENGINE_VERSION = '0.89.0-9';
33
+ const PROMPTBOOK_ENGINE_VERSION = '0.92.0-10';
34
34
  /**
35
35
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
36
36
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -76,6 +76,7 @@ const ADMIN_EMAIL = 'pavol@ptbk.io';
76
76
  * @public exported from `@promptbook/core`
77
77
  */
78
78
  const ADMIN_GITHUB_NAME = 'hejny';
79
+ // <- TODO: [๐ŸŠ] Pick the best claim
79
80
  /**
80
81
  * When the title is not provided, the default title is used
81
82
  *
@@ -120,6 +121,7 @@ const VALUE_STRINGS = {
120
121
  infinity: '(infinity; โˆž)',
121
122
  negativeInfinity: '(negative infinity; -โˆž)',
122
123
  unserializable: '(unserializable value)',
124
+ circular: '(circular JSON)',
123
125
  };
124
126
  /**
125
127
  * Small number limit
@@ -159,7 +161,7 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
159
161
  */
160
162
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
161
163
  // <- TODO: [๐Ÿ•] Make also `BOOKS_DIRNAME_ALTERNATIVES`
162
- // TODO: !!!!!! Just .promptbook dir, hardocode others
164
+ // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
163
165
  /**
164
166
  * Where to store the temporary downloads
165
167
  *
@@ -297,6 +299,45 @@ class UnexpectedError extends Error {
297
299
  }
298
300
  }
299
301
 
302
+ /**
303
+ * Converts a JavaScript Object Notation (JSON) string into an object.
304
+ *
305
+ * Note: This is wrapper around `JSON.parse()` with better error and type handling
306
+ *
307
+ * @public exported from `@promptbook/utils`
308
+ */
309
+ function jsonParse(value) {
310
+ if (value === undefined) {
311
+ throw new Error(`Can not parse JSON from undefined value.`);
312
+ }
313
+ else if (typeof value !== 'string') {
314
+ console.error('Can not parse JSON from non-string value.', { text: value });
315
+ throw new Error(spaceTrim(`
316
+ Can not parse JSON from non-string value.
317
+
318
+ The value type: ${typeof value}
319
+ See more in console.
320
+ `));
321
+ }
322
+ try {
323
+ return JSON.parse(value);
324
+ }
325
+ catch (error) {
326
+ if (!(error instanceof Error)) {
327
+ throw error;
328
+ }
329
+ throw new Error(spaceTrim((block) => `
330
+ ${block(error.message)}
331
+
332
+ The JSON text:
333
+ ${block(value)}
334
+ `));
335
+ }
336
+ }
337
+ /**
338
+ * TODO: !!!! Use in Promptbook.studio
339
+ */
340
+
300
341
  /**
301
342
  * Orders JSON object by keys
302
343
  *
@@ -339,6 +380,54 @@ function $deepFreeze(objectValue) {
339
380
  * TODO: [๐Ÿง ] Is there a way how to meaningfully test this utility
340
381
  */
341
382
 
383
+ /**
384
+ * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
385
+ *
386
+ * @public exported from `@promptbook/core`
387
+ */
388
+ class WrappedError extends Error {
389
+ constructor(whatWasThrown) {
390
+ const tag = `[๐Ÿคฎ]`;
391
+ console.error(tag, whatWasThrown);
392
+ super(spaceTrim$1(`
393
+ Non-Error object was thrown
394
+
395
+ Note: Look for ${tag} in the console for more details
396
+ Please report issue on ${ADMIN_EMAIL}
397
+ `));
398
+ this.name = 'WrappedError';
399
+ Object.setPrototypeOf(this, WrappedError.prototype);
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Helper used in catch blocks to assert that the error is an instance of `Error`
405
+ *
406
+ * @param whatWasThrown Any object that was thrown
407
+ * @returns Nothing if the error is an instance of `Error`
408
+ * @throws `WrappedError` or `UnexpectedError` if the error is not standard
409
+ *
410
+ * @private within the repository
411
+ */
412
+ function assertsError(whatWasThrown) {
413
+ // Case 1: Handle error which was rethrown as `WrappedError`
414
+ if (whatWasThrown instanceof WrappedError) {
415
+ const wrappedError = whatWasThrown;
416
+ throw wrappedError;
417
+ }
418
+ // Case 2: Handle unexpected errors
419
+ if (whatWasThrown instanceof UnexpectedError) {
420
+ const unexpectedError = whatWasThrown;
421
+ throw unexpectedError;
422
+ }
423
+ // Case 3: Handle standard errors - keep them up to consumer
424
+ if (whatWasThrown instanceof Error) {
425
+ return;
426
+ }
427
+ // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
428
+ throw new WrappedError(whatWasThrown);
429
+ }
430
+
342
431
  /**
343
432
  * Checks if the value is [๐Ÿš‰] serializable as JSON
344
433
  * If not, throws an UnexpectedError with a rich error message and tracking
@@ -430,9 +519,7 @@ function checkSerializableAsJson(options) {
430
519
  JSON.stringify(value); // <- TODO: [0]
431
520
  }
432
521
  catch (error) {
433
- if (!(error instanceof Error)) {
434
- throw error;
435
- }
522
+ assertsError(error);
436
523
  throw new UnexpectedError(spaceTrim((block) => `
437
524
  \`${name}\` is not serializable
438
525
 
@@ -1040,7 +1127,7 @@ async function loadArchive(filePath, fs) {
1040
1127
  if (!indexFile) {
1041
1128
  throw new UnexpectedError(`Archive does not contain 'index.book.json' file`);
1042
1129
  }
1043
- const collectionJson = JSON.parse(await indexFile.async('text'));
1130
+ const collectionJson = jsonParse(await indexFile.async('text'));
1044
1131
  for (const pipeline of collectionJson) {
1045
1132
  validatePipeline(pipeline);
1046
1133
  }
@@ -1050,7 +1137,7 @@ async function loadArchive(filePath, fs) {
1050
1137
  * Note: [๐ŸŸข] Code in this file should never be never released in packages that could be imported into browser environment
1051
1138
  */
1052
1139
 
1053
- 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"}];
1140
+ var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [๐Ÿ†] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [๐Ÿ†] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n```json\n{availableModels}\n```\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpfull assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelsRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"โœ Convert Knowledge-piece to title\" but \"โœ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"โœ Convert Knowledge-piece to title\" but \"โœ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
1054
1141
 
1055
1142
  /**
1056
1143
  * Checks if value is valid email
@@ -1119,6 +1206,9 @@ function isValidFilePath(filename) {
1119
1206
  /**
1120
1207
  * Function isValidJsonString will tell you if the string is valid JSON or not
1121
1208
  *
1209
+ * @param value The string to check
1210
+ * @returns True if the string is a valid JSON string, false otherwise
1211
+ *
1122
1212
  * @public exported from `@promptbook/utils`
1123
1213
  */
1124
1214
  function isValidJsonString(value /* <- [๐Ÿ‘จโ€โš–๏ธ] */) {
@@ -1127,9 +1217,7 @@ function isValidJsonString(value /* <- [๐Ÿ‘จโ€โš–๏ธ] */) {
1127
1217
  return true;
1128
1218
  }
1129
1219
  catch (error) {
1130
- if (!(error instanceof Error)) {
1131
- throw error;
1132
- }
1220
+ assertsError(error);
1133
1221
  if (error.message.includes('Unexpected token')) {
1134
1222
  return false;
1135
1223
  }
@@ -1400,7 +1488,7 @@ function extractParameterNames(template) {
1400
1488
  */
1401
1489
  function unpreparePipeline(pipeline) {
1402
1490
  let { personas, knowledgeSources, tasks } = pipeline;
1403
- personas = personas.map((persona) => ({ ...persona, modelRequirements: undefined, preparationIds: undefined }));
1491
+ personas = personas.map((persona) => ({ ...persona, modelsRequirements: undefined, preparationIds: undefined }));
1404
1492
  knowledgeSources = knowledgeSources.map((knowledgeSource) => ({ ...knowledgeSource, preparationIds: undefined }));
1405
1493
  tasks = tasks.map((task) => {
1406
1494
  let { dependentParameterNames } = task;
@@ -1576,7 +1664,7 @@ function isPipelinePrepared(pipeline) {
1576
1664
  if (pipeline.title === undefined || pipeline.title === '' || pipeline.title === DEFAULT_BOOK_TITLE) {
1577
1665
  return false;
1578
1666
  }
1579
- if (!pipeline.personas.every((persona) => persona.modelRequirements !== undefined)) {
1667
+ if (!pipeline.personas.every((persona) => persona.modelsRequirements !== undefined)) {
1580
1668
  return false;
1581
1669
  }
1582
1670
  if (!pipeline.knowledgeSources.every((knowledgeSource) => knowledgeSource.preparationIds !== undefined)) {
@@ -1633,7 +1721,7 @@ function jsonStringsToJsons(object) {
1633
1721
  const newObject = { ...object };
1634
1722
  for (const [key, value] of Object.entries(object)) {
1635
1723
  if (typeof value === 'string' && isValidJsonString(value)) {
1636
- newObject[key] = JSON.parse(value);
1724
+ newObject[key] = jsonParse(value);
1637
1725
  }
1638
1726
  else {
1639
1727
  newObject[key] = jsonStringsToJsons(value);
@@ -1661,7 +1749,7 @@ class PipelineExecutionError extends Error {
1661
1749
  }
1662
1750
  }
1663
1751
  /**
1664
- * TODO: !!!!!! Add id to all errors
1752
+ * TODO: [๐Ÿง ][๐ŸŒ‚] Add id to all errors
1665
1753
  */
1666
1754
 
1667
1755
  /**
@@ -1831,7 +1919,10 @@ const PROMPTBOOK_ERRORS = {
1831
1919
  PipelineExecutionError,
1832
1920
  PipelineLogicError,
1833
1921
  PipelineUrlError,
1922
+ AuthenticationError,
1923
+ PromptbookFetchError,
1834
1924
  UnexpectedError,
1925
+ WrappedError,
1835
1926
  // TODO: [๐Ÿช‘]> VersionMismatchError,
1836
1927
  };
1837
1928
  /**
@@ -1848,8 +1939,6 @@ const COMMON_JAVASCRIPT_ERRORS = {
1848
1939
  TypeError,
1849
1940
  URIError,
1850
1941
  AggregateError,
1851
- AuthenticationError,
1852
- PromptbookFetchError,
1853
1942
  /*
1854
1943
  Note: Not widely supported
1855
1944
  > InternalError,
@@ -1972,8 +2061,8 @@ function createTask(options) {
1972
2061
  updatedAt = new Date();
1973
2062
  errors.push(...executionResult.errors);
1974
2063
  warnings.push(...executionResult.warnings);
1975
- // <- TODO: !!! Only unique errors and warnings should be added (or filtered)
1976
- // TODO: [๐Ÿง ] !!! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
2064
+ // <- TODO: [๐ŸŒ‚] Only unique errors and warnings should be added (or filtered)
2065
+ // TODO: [๐Ÿง ] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
1977
2066
  // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
1978
2067
  // And delete `ExecutionTask.currentValue.preparedPipeline`
1979
2068
  assertsTaskSuccessful(executionResult);
@@ -1983,6 +2072,7 @@ function createTask(options) {
1983
2072
  partialResultSubject.next(executionResult);
1984
2073
  }
1985
2074
  catch (error) {
2075
+ assertsError(error);
1986
2076
  status = 'ERROR';
1987
2077
  errors.push(error);
1988
2078
  partialResultSubject.error(error);
@@ -2128,13 +2218,19 @@ function valueToString(value) {
2128
2218
  return value.toISOString();
2129
2219
  }
2130
2220
  else {
2131
- return JSON.stringify(value);
2221
+ try {
2222
+ return JSON.stringify(value);
2223
+ }
2224
+ catch (error) {
2225
+ if (error instanceof TypeError && error.message.includes('circular structure')) {
2226
+ return VALUE_STRINGS.circular;
2227
+ }
2228
+ throw error;
2229
+ }
2132
2230
  }
2133
2231
  }
2134
2232
  catch (error) {
2135
- if (!(error instanceof Error)) {
2136
- throw error;
2137
- }
2233
+ assertsError(error);
2138
2234
  console.error(error);
2139
2235
  return VALUE_STRINGS.unserializable;
2140
2236
  }
@@ -2306,9 +2402,7 @@ function extractVariablesFromJavascript(script) {
2306
2402
  }
2307
2403
  }
2308
2404
  catch (error) {
2309
- if (!(error instanceof Error)) {
2310
- throw error;
2311
- }
2405
+ assertsError(error);
2312
2406
  throw new ParseError(spaceTrim$1((block) => `
2313
2407
  Can not extract variables from the script
2314
2408
  ${block(error.stack || error.message)}
@@ -2427,6 +2521,46 @@ const MANDATORY_CSV_SETTINGS = Object.freeze({
2427
2521
  // encoding: 'utf-8',
2428
2522
  });
2429
2523
 
2524
+ /**
2525
+ * Function to check if a string is valid CSV
2526
+ *
2527
+ * @param value The string to check
2528
+ * @returns True if the string is a valid CSV string, false otherwise
2529
+ *
2530
+ * @public exported from `@promptbook/utils`
2531
+ */
2532
+ function isValidCsvString(value) {
2533
+ try {
2534
+ // A simple check for CSV format: at least one comma and no invalid characters
2535
+ if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
2536
+ return true;
2537
+ }
2538
+ return false;
2539
+ }
2540
+ catch (error) {
2541
+ assertsError(error);
2542
+ return false;
2543
+ }
2544
+ }
2545
+
2546
+ /**
2547
+ * Converts a CSV string into an object
2548
+ *
2549
+ * Note: This is wrapper around `papaparse.parse()` with better autohealing
2550
+ *
2551
+ * @private - for now until `@promptbook/csv` is released
2552
+ */
2553
+ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO: Make CSV Schemas */) {
2554
+ settings = { ...settings, ...MANDATORY_CSV_SETTINGS };
2555
+ // Note: Autoheal invalid '\n' characters
2556
+ if (settings.newline && !settings.newline.includes('\r') && value.includes('\r')) {
2557
+ console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
2558
+ value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
2559
+ }
2560
+ const csv = parse(value, settings);
2561
+ return csv;
2562
+ }
2563
+
2430
2564
  /**
2431
2565
  * Definition for CSV spreadsheet
2432
2566
  *
@@ -2437,7 +2571,7 @@ const CsvFormatDefinition = {
2437
2571
  formatName: 'CSV',
2438
2572
  aliases: ['SPREADSHEET', 'TABLE'],
2439
2573
  isValid(value, settings, schema) {
2440
- return true;
2574
+ return isValidCsvString(value);
2441
2575
  },
2442
2576
  canBeValid(partialValue, settings, schema) {
2443
2577
  return true;
@@ -2449,8 +2583,7 @@ const CsvFormatDefinition = {
2449
2583
  {
2450
2584
  subvalueName: 'ROW',
2451
2585
  async mapValues(value, outputParameterName, settings, mapCallback) {
2452
- // TODO: [๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ] DRY csv parsing
2453
- const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
2586
+ const csv = csvParse(value, settings);
2454
2587
  if (csv.errors.length !== 0) {
2455
2588
  throw new CsvFormatError(spaceTrim((block) => `
2456
2589
  CSV parsing error
@@ -2480,8 +2613,7 @@ const CsvFormatDefinition = {
2480
2613
  {
2481
2614
  subvalueName: 'CELL',
2482
2615
  async mapValues(value, outputParameterName, settings, mapCallback) {
2483
- // TODO: [๐Ÿ‘จ๐Ÿพโ€๐Ÿคโ€๐Ÿ‘จ๐Ÿผ] DRY csv parsing
2484
- const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
2616
+ const csv = csvParse(value, settings);
2485
2617
  if (csv.errors.length !== 0) {
2486
2618
  throw new CsvFormatError(spaceTrim((block) => `
2487
2619
  CSV parsing error
@@ -2591,6 +2723,30 @@ const TextFormatDefinition = {
2591
2723
  * TODO: [๐Ÿข] Allow to expect something inside each item of list and other formats
2592
2724
  */
2593
2725
 
2726
+ /**
2727
+ * Function to check if a string is valid XML
2728
+ *
2729
+ * @param value
2730
+ * @returns True if the string is a valid XML string, false otherwise
2731
+ *
2732
+ * @public exported from `@promptbook/utils`
2733
+ */
2734
+ function isValidXmlString(value) {
2735
+ try {
2736
+ const parser = new DOMParser();
2737
+ const parsedDocument = parser.parseFromString(value, 'application/xml');
2738
+ const parserError = parsedDocument.getElementsByTagName('parsererror');
2739
+ if (parserError.length > 0) {
2740
+ return false;
2741
+ }
2742
+ return true;
2743
+ }
2744
+ catch (error) {
2745
+ assertsError(error);
2746
+ return false;
2747
+ }
2748
+ }
2749
+
2594
2750
  /**
2595
2751
  * Definition for XML format
2596
2752
  *
@@ -2600,7 +2756,7 @@ const XmlFormatDefinition = {
2600
2756
  formatName: 'XML',
2601
2757
  mimeType: 'application/xml',
2602
2758
  isValid(value, settings, schema) {
2603
- return true;
2759
+ return isValidXmlString(value);
2604
2760
  },
2605
2761
  canBeValid(partialValue, settings, schema) {
2606
2762
  return true;
@@ -2797,14 +2953,15 @@ class MultipleLlmExecutionTools {
2797
2953
  }
2798
2954
  }
2799
2955
  catch (error) {
2800
- if (!(error instanceof Error) || error instanceof UnexpectedError) {
2956
+ assertsError(error);
2957
+ if (error instanceof UnexpectedError) {
2801
2958
  throw error;
2802
2959
  }
2803
2960
  errors.push({ llmExecutionTools, error });
2804
2961
  }
2805
2962
  }
2806
2963
  if (errors.length === 1) {
2807
- throw errors[0];
2964
+ throw errors[0].error;
2808
2965
  }
2809
2966
  else if (errors.length > 1) {
2810
2967
  throw new PipelineExecutionError(
@@ -3661,9 +3818,7 @@ async function executeAttempts(options) {
3661
3818
  break scripts;
3662
3819
  }
3663
3820
  catch (error) {
3664
- if (!(error instanceof Error)) {
3665
- throw error;
3666
- }
3821
+ assertsError(error);
3667
3822
  if (error instanceof UnexpectedError) {
3668
3823
  throw error;
3669
3824
  }
@@ -3733,9 +3888,7 @@ async function executeAttempts(options) {
3733
3888
  break scripts;
3734
3889
  }
3735
3890
  catch (error) {
3736
- if (!(error instanceof Error)) {
3737
- throw error;
3738
- }
3891
+ assertsError(error);
3739
3892
  if (error instanceof UnexpectedError) {
3740
3893
  throw error;
3741
3894
  }
@@ -3978,13 +4131,79 @@ async function getExamplesForTask(task) {
3978
4131
  /**
3979
4132
  * @@@
3980
4133
  *
4134
+ * Here is the place where RAG (retrieval-augmented generation) happens
4135
+ *
3981
4136
  * @private internal utility of `createPipelineExecutor`
3982
4137
  */
3983
4138
  async function getKnowledgeForTask(options) {
3984
- const { preparedPipeline, task } = options;
3985
- return preparedPipeline.knowledgePieces.map(({ content }) => `- ${content}`).join('\n');
4139
+ const { tools, preparedPipeline, task } = options;
4140
+ const firstKnowlegePiece = preparedPipeline.knowledgePieces[0];
4141
+ const firstKnowlegeIndex = firstKnowlegePiece === null || firstKnowlegePiece === void 0 ? void 0 : firstKnowlegePiece.index[0];
4142
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model, use also keyword search
4143
+ if (firstKnowlegePiece === undefined || firstKnowlegeIndex === undefined) {
4144
+ return 'No knowledge pieces found';
4145
+ }
4146
+ // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
4147
+ const _llms = arrayableToArray(tools.llm);
4148
+ const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
4149
+ const taskEmbeddingPrompt = {
4150
+ title: 'Knowledge Search',
4151
+ modelRequirements: {
4152
+ modelVariant: 'EMBEDDING',
4153
+ modelName: firstKnowlegeIndex.modelName,
4154
+ },
4155
+ content: task.content,
4156
+ parameters: {
4157
+ /* !!!!!!!! */
4158
+ },
4159
+ };
4160
+ const taskEmbeddingResult = await llmTools.callEmbeddingModel(taskEmbeddingPrompt);
4161
+ const knowledgePiecesWithRelevance = preparedPipeline.knowledgePieces.map((knowledgePiece) => {
4162
+ const { index } = knowledgePiece;
4163
+ const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowlegeIndex.modelName);
4164
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model
4165
+ if (knowledgePieceIndex === undefined) {
4166
+ return {
4167
+ content: knowledgePiece.content,
4168
+ relevance: 0,
4169
+ };
4170
+ }
4171
+ const relevance = computeCosineSimilarity(knowledgePieceIndex.position, taskEmbeddingResult.content);
4172
+ return {
4173
+ content: knowledgePiece.content,
4174
+ relevance,
4175
+ };
4176
+ });
4177
+ const knowledgePiecesSorted = knowledgePiecesWithRelevance.sort((a, b) => a.relevance - b.relevance);
4178
+ const knowledgePiecesLimited = knowledgePiecesSorted.slice(0, 5);
4179
+ console.log('!!! Embedding', {
4180
+ task,
4181
+ taskEmbeddingPrompt,
4182
+ taskEmbeddingResult,
4183
+ firstKnowlegePiece,
4184
+ firstKnowlegeIndex,
4185
+ knowledgePiecesWithRelevance,
4186
+ knowledgePiecesSorted,
4187
+ knowledgePiecesLimited,
4188
+ });
4189
+ return knowledgePiecesLimited.map(({ content }) => `- ${content}`).join('\n');
3986
4190
  // <- TODO: [๐Ÿง ] Some smart aggregation of knowledge pieces, single-line vs multi-line vs mixed
3987
4191
  }
4192
+ // TODO: !!!!!! Annotate + to new file
4193
+ function computeCosineSimilarity(embeddingVector1, embeddingVector2) {
4194
+ if (embeddingVector1.length !== embeddingVector2.length) {
4195
+ throw new TypeError('Embedding vectors must have the same length');
4196
+ }
4197
+ const dotProduct = embeddingVector1.reduce((sum, value, index) => sum + value * embeddingVector2[index], 0);
4198
+ const magnitude1 = Math.sqrt(embeddingVector1.reduce((sum, value) => sum + value * value, 0));
4199
+ const magnitude2 = Math.sqrt(embeddingVector2.reduce((sum, value) => sum + value * value, 0));
4200
+ return 1 - dotProduct / (magnitude1 * magnitude2);
4201
+ }
4202
+ /**
4203
+ * TODO: !!!! Verify if this is working
4204
+ * TODO: [โ™จ] Implement Better - use keyword search
4205
+ * TODO: [โ™จ] Examples of values
4206
+ */
3988
4207
 
3989
4208
  /**
3990
4209
  * @@@
@@ -3992,9 +4211,9 @@ async function getKnowledgeForTask(options) {
3992
4211
  * @private internal utility of `createPipelineExecutor`
3993
4212
  */
3994
4213
  async function getReservedParametersForTask(options) {
3995
- const { preparedPipeline, task, pipelineIdentification } = options;
4214
+ const { tools, preparedPipeline, task, pipelineIdentification } = options;
3996
4215
  const context = await getContextForTask(); // <- [๐Ÿ]
3997
- const knowledge = await getKnowledgeForTask({ preparedPipeline, task });
4216
+ const knowledge = await getKnowledgeForTask({ tools, preparedPipeline, task });
3998
4217
  const examples = await getExamplesForTask();
3999
4218
  const currentDate = new Date().toISOString(); // <- TODO: [๐Ÿง ][๐Ÿ’ฉ] Better
4000
4219
  const modelName = RESERVED_PARAMETER_MISSING_VALUE;
@@ -4056,6 +4275,7 @@ async function executeTask(options) {
4056
4275
  }
4057
4276
  const definedParameters = Object.freeze({
4058
4277
  ...(await getReservedParametersForTask({
4278
+ tools,
4059
4279
  preparedPipeline,
4060
4280
  task: currentTask,
4061
4281
  pipelineIdentification,
@@ -4356,9 +4576,7 @@ async function executePipeline(options) {
4356
4576
  await Promise.all(resolving);
4357
4577
  }
4358
4578
  catch (error /* <- Note: [3] */) {
4359
- if (!(error instanceof Error)) {
4360
- throw error;
4361
- }
4579
+ assertsError(error);
4362
4580
  // Note: No need to rethrow UnexpectedError
4363
4581
  // if (error instanceof UnexpectedError) {
4364
4582
  // Note: Count usage, [๐Ÿง ] Maybe put to separate function executionReportJsonToUsage + DRY [๐Ÿคนโ€โ™‚๏ธ]
@@ -4614,27 +4832,48 @@ async function preparePersona(personaDescription, tools, options) {
4614
4832
  pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-persona.book'),
4615
4833
  tools,
4616
4834
  });
4617
- // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
4618
4835
  const _llms = arrayableToArray(tools.llm);
4619
4836
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
4620
- const availableModels = await llmTools.listModels();
4621
- const availableModelNames = availableModels
4837
+ const availableModels = (await llmTools.listModels())
4622
4838
  .filter(({ modelVariant }) => modelVariant === 'CHAT')
4623
- .map(({ modelName }) => modelName)
4624
- .join(',');
4625
- const result = await preparePersonaExecutor({ availableModelNames, personaDescription }).asPromise();
4839
+ .map(({ modelName, modelDescription }) => ({
4840
+ modelName,
4841
+ modelDescription,
4842
+ // <- Note: `modelTitle` and `modelVariant` is not relevant for this task
4843
+ }));
4844
+ const result = await preparePersonaExecutor({
4845
+ availableModels /* <- Note: Passing as JSON */,
4846
+ personaDescription,
4847
+ }).asPromise();
4626
4848
  const { outputParameters } = result;
4627
- const { modelRequirements: modelRequirementsRaw } = outputParameters;
4628
- const modelRequirements = JSON.parse(modelRequirementsRaw);
4849
+ const { modelsRequirements: modelsRequirementsJson } = outputParameters;
4850
+ let modelsRequirementsUnchecked = jsonParse(modelsRequirementsJson);
4629
4851
  if (isVerbose) {
4630
- console.info(`PERSONA ${personaDescription}`, modelRequirements);
4852
+ console.info(`PERSONA ${personaDescription}`, modelsRequirementsUnchecked);
4631
4853
  }
4632
- const { modelName, systemMessage, temperature } = modelRequirements;
4633
- return {
4854
+ if (!Array.isArray(modelsRequirementsUnchecked)) {
4855
+ // <- TODO: Book should have syntax and system to enforce shape of JSON
4856
+ modelsRequirementsUnchecked = [modelsRequirementsUnchecked];
4857
+ /*
4858
+ throw new UnexpectedError(
4859
+ spaceTrim(
4860
+ (block) => `
4861
+ Invalid \`modelsRequirements\`:
4862
+
4863
+ \`\`\`json
4864
+ ${block(JSON.stringify(modelsRequirementsUnchecked, null, 4))}
4865
+ \`\`\`
4866
+ `,
4867
+ ),
4868
+ );
4869
+ */
4870
+ }
4871
+ const modelsRequirements = modelsRequirementsUnchecked.map((modelRequirements) => ({
4634
4872
  modelVariant: 'CHAT',
4635
- modelName,
4636
- systemMessage,
4637
- temperature,
4873
+ ...modelRequirements,
4874
+ }));
4875
+ return {
4876
+ modelsRequirements,
4638
4877
  };
4639
4878
  }
4640
4879
  /**
@@ -5068,9 +5307,7 @@ const promptbookFetch = async (urlOrRequest, init) => {
5068
5307
  return await fetch(urlOrRequest, init);
5069
5308
  }
5070
5309
  catch (error) {
5071
- if (!(error instanceof Error)) {
5072
- throw error;
5073
- }
5310
+ assertsError(error);
5074
5311
  let url;
5075
5312
  if (typeof urlOrRequest === 'string') {
5076
5313
  url = urlOrRequest;
@@ -5199,7 +5436,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
5199
5436
  > },
5200
5437
  */
5201
5438
  async asJson() {
5202
- return JSON.parse(await tools.fs.readFile(filename, 'utf-8'));
5439
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
5203
5440
  },
5204
5441
  async asText() {
5205
5442
  return await tools.fs.readFile(filename, 'utf-8');
@@ -5301,9 +5538,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
5301
5538
  knowledgePreparedUnflatten[index] = pieces;
5302
5539
  }
5303
5540
  catch (error) {
5304
- if (!(error instanceof Error)) {
5305
- throw error;
5306
- }
5541
+ assertsError(error);
5307
5542
  console.warn(error);
5308
5543
  // <- TODO: [๐Ÿฎ] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
5309
5544
  }
@@ -5459,14 +5694,14 @@ async function preparePipeline(pipeline, tools, options) {
5459
5694
  // TODO: [๐Ÿ–Œ][๐Ÿง ] Implement some `mapAsync` function
5460
5695
  const preparedPersonas = new Array(personas.length);
5461
5696
  await forEachAsync(personas, { maxParallelCount /* <- TODO: [๐Ÿช‚] When there are subtasks, this maximul limit can be broken */ }, async (persona, index) => {
5462
- const modelRequirements = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
5697
+ const { modelsRequirements } = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
5463
5698
  rootDirname,
5464
5699
  maxParallelCount /* <- TODO: [๐Ÿช‚] */,
5465
5700
  isVerbose,
5466
5701
  });
5467
5702
  const preparedPersona = {
5468
5703
  ...persona,
5469
- modelRequirements,
5704
+ modelsRequirements,
5470
5705
  preparationIds: [/* TODO: [๐ŸงŠ] -> */ currentPreparation.id],
5471
5706
  // <- TODO: [๐Ÿ™] Make some standard order of json properties
5472
5707
  };
@@ -6101,6 +6336,8 @@ function parseNumber(value) {
6101
6336
  */
6102
6337
 
6103
6338
  /**
6339
+ import { WrappedError } from '../../errors/WrappedError';
6340
+ import { assertsError } from '../../errors/assertsError';
6104
6341
  * Parses the expect command
6105
6342
  *
6106
6343
  * @see `documentationUrl` for more details
@@ -6192,9 +6429,7 @@ const expectCommandParser = {
6192
6429
  };
6193
6430
  }
6194
6431
  catch (error) {
6195
- if (!(error instanceof Error)) {
6196
- throw error;
6197
- }
6432
+ assertsError(error);
6198
6433
  throw new ParseError(spaceTrim((block) => `
6199
6434
  Invalid FORMAT command
6200
6435
  ${block(error.message)}:
@@ -9175,9 +9410,7 @@ async function locateAppOnLinux({ linuxWhich, }) {
9175
9410
  return result.trim();
9176
9411
  }
9177
9412
  catch (error) {
9178
- if (!(error instanceof Error)) {
9179
- throw error;
9180
- }
9413
+ assertsError(error);
9181
9414
  return null;
9182
9415
  }
9183
9416
  }
@@ -9255,9 +9488,7 @@ async function locateAppOnMacOs({ macOsName, }) {
9255
9488
  return result.trim() + toExec;
9256
9489
  }
9257
9490
  catch (error) {
9258
- if (!(error instanceof Error)) {
9259
- throw error;
9260
- }
9491
+ assertsError(error);
9261
9492
  return null;
9262
9493
  }
9263
9494
  }
@@ -9288,9 +9519,7 @@ async function locateAppOnWindows({ appName, windowsSuffix, }) {
9288
9519
  throw new Error(`Can not locate app ${appName} on Windows.`);
9289
9520
  }
9290
9521
  catch (error) {
9291
- if (!(error instanceof Error)) {
9292
- throw error;
9293
- }
9522
+ assertsError(error);
9294
9523
  return null;
9295
9524
  }
9296
9525
  }
@@ -9581,13 +9810,13 @@ function $registeredLlmToolsMessage() {
9581
9810
  /**
9582
9811
  * Provides the path to the `.env` file
9583
9812
  *
9584
- * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access .env file
9813
+ * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
9585
9814
  *
9586
9815
  * @private within the repository - for CLI utils
9587
9816
  */
9588
- async function $provideEnvFilepath() {
9817
+ async function $provideEnvFilename() {
9589
9818
  if (!$isRunningInNode()) {
9590
- throw new EnvironmentMismatchError('Function `$provideEnvFilepath` works only in Node.js environment');
9819
+ throw new EnvironmentMismatchError('Function `$provideEnvFilename` works only in Node.js environment');
9591
9820
  }
9592
9821
  const envFilePatterns = [
9593
9822
  '.env',
@@ -9626,7 +9855,7 @@ async function $provideEnvFilepath() {
9626
9855
  * @@@
9627
9856
  *
9628
9857
  * @@@ .env
9629
- * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access .env file
9858
+ * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
9630
9859
  *
9631
9860
  * It looks for environment variables:
9632
9861
  * - `process.env.OPENAI_API_KEY`
@@ -9640,7 +9869,7 @@ async function $provideLlmToolsConfigurationFromEnv() {
9640
9869
  if (!$isRunningInNode()) {
9641
9870
  throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
9642
9871
  }
9643
- const envFilepath = await $provideEnvFilepath();
9872
+ const envFilepath = await $provideEnvFilename();
9644
9873
  if (envFilepath !== null) {
9645
9874
  dotenv.config({ path: envFilepath });
9646
9875
  }
@@ -9711,7 +9940,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
9711
9940
  * Note: This function is not cached, every call creates new instance of `MultipleLlmExecutionTools`
9712
9941
  *
9713
9942
  * @@@ .env
9714
- * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access .env file
9943
+ * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
9715
9944
  *
9716
9945
  * It looks for environment variables:
9717
9946
  * - `process.env.OPENAI_API_KEY`
@@ -10245,9 +10474,7 @@ class JavascriptEvalExecutionTools {
10245
10474
  }
10246
10475
  }
10247
10476
  catch (error) {
10248
- if (!(error instanceof Error)) {
10249
- throw error;
10250
- }
10477
+ assertsError(error);
10251
10478
  if (error instanceof ReferenceError) {
10252
10479
  const undefinedName = error.message.split(' ')[0];
10253
10480
  /*
@@ -10522,9 +10749,7 @@ async function createCollectionFromDirectory(rootPath, tools, options) {
10522
10749
  // ---
10523
10750
  }
10524
10751
  catch (error) {
10525
- if (!(error instanceof Error)) {
10526
- throw error;
10527
- }
10752
+ assertsError(error);
10528
10753
  // TODO: [7] DRY
10529
10754
  const wrappedErrorMessage = spaceTrim((block) => `
10530
10755
  ${error.name} in pipeline ${fileName.split('\\').join('/')}โ :
@@ -10615,9 +10840,7 @@ async function createCollectionFromDirectory(rootPath, tools, options) {
10615
10840
  }
10616
10841
  }
10617
10842
  catch (error) {
10618
- if (!(error instanceof Error)) {
10619
- throw error;
10620
- }
10843
+ assertsError(error);
10621
10844
  // TODO: [7] DRY
10622
10845
  const wrappedErrorMessage = spaceTrim((block) => `
10623
10846
  ${error.name} in pipeline ${fileName.split('\\').join('/')}โ :
@@ -10733,7 +10956,7 @@ class FileCacheStorage {
10733
10956
  return null;
10734
10957
  }
10735
10958
  const fileContent = await readFile(filename, 'utf-8');
10736
- const value = JSON.parse(fileContent);
10959
+ const value = jsonParse(fileContent);
10737
10960
  // TODO: [๐ŸŒ—]
10738
10961
  return value;
10739
10962
  }