@promptbook/cli 0.92.0-3 → 0.92.0-31

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 (172) hide show
  1. package/esm/index.es.js +1661 -526
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/typings/src/_packages/browser.index.d.ts +2 -0
  4. package/esm/typings/src/_packages/core.index.d.ts +22 -6
  5. package/esm/typings/src/_packages/deepseek.index.d.ts +2 -0
  6. package/esm/typings/src/_packages/google.index.d.ts +2 -0
  7. package/esm/typings/src/_packages/types.index.d.ts +4 -2
  8. package/esm/typings/src/_packages/utils.index.d.ts +2 -0
  9. package/esm/typings/src/cli/common/$provideLlmToolsForCli.d.ts +1 -1
  10. package/esm/typings/src/collection/PipelineCollection.d.ts +0 -2
  11. package/esm/typings/src/collection/SimplePipelineCollection.d.ts +1 -1
  12. package/esm/typings/src/commands/FOREACH/ForeachJson.d.ts +6 -6
  13. package/esm/typings/src/commands/FOREACH/foreachCommandParser.d.ts +0 -2
  14. package/esm/typings/src/commands/FORMFACTOR/formfactorCommandParser.d.ts +1 -1
  15. package/esm/typings/src/commands/_BOILERPLATE/boilerplateCommandParser.d.ts +1 -1
  16. package/esm/typings/src/commands/_common/types/CommandParser.d.ts +36 -28
  17. package/esm/typings/src/config.d.ts +41 -11
  18. package/esm/typings/src/constants.d.ts +43 -2
  19. package/esm/typings/src/conversion/archive/loadArchive.d.ts +2 -2
  20. package/esm/typings/src/errors/0-BoilerplateError.d.ts +2 -2
  21. package/esm/typings/src/executables/$provideExecutablesForNode.d.ts +1 -1
  22. package/esm/typings/src/executables/apps/locateLibreoffice.d.ts +2 -1
  23. package/esm/typings/src/executables/apps/locatePandoc.d.ts +2 -1
  24. package/esm/typings/src/executables/locateApp.d.ts +2 -2
  25. package/esm/typings/src/executables/platforms/locateAppOnLinux.d.ts +2 -1
  26. package/esm/typings/src/executables/platforms/locateAppOnMacOs.d.ts +2 -1
  27. package/esm/typings/src/executables/platforms/locateAppOnWindows.d.ts +2 -1
  28. package/esm/typings/src/execution/AbstractTaskResult.d.ts +1 -1
  29. package/esm/typings/src/execution/CommonToolsOptions.d.ts +5 -1
  30. package/esm/typings/src/execution/LlmExecutionToolsConstructor.d.ts +2 -1
  31. package/esm/typings/src/execution/PipelineExecutorResult.d.ts +4 -2
  32. package/esm/typings/src/execution/createPipelineExecutor/$OngoingTaskResult.d.ts +12 -9
  33. package/esm/typings/src/execution/createPipelineExecutor/10-executePipeline.d.ts +13 -10
  34. package/esm/typings/src/execution/createPipelineExecutor/20-executeTask.d.ts +12 -9
  35. package/esm/typings/src/execution/createPipelineExecutor/30-executeFormatSubvalues.d.ts +15 -3
  36. package/esm/typings/src/execution/createPipelineExecutor/40-executeAttempts.d.ts +20 -14
  37. package/esm/typings/src/execution/createPipelineExecutor/computeCosineSimilarity.d.ts +13 -0
  38. package/esm/typings/src/execution/createPipelineExecutor/filterJustOutputParameters.d.ts +7 -6
  39. package/esm/typings/src/execution/createPipelineExecutor/getContextForTask.d.ts +5 -1
  40. package/esm/typings/src/execution/createPipelineExecutor/getExamplesForTask.d.ts +1 -1
  41. package/esm/typings/src/execution/createPipelineExecutor/getKnowledgeForTask.d.ts +21 -5
  42. package/esm/typings/src/execution/createPipelineExecutor/getReservedParametersForTask.d.ts +19 -5
  43. package/esm/typings/src/execution/createPipelineExecutor/knowledgePiecesToString.d.ts +9 -0
  44. package/esm/typings/src/execution/translation/automatic-translate/automatic-translators/LindatAutomaticTranslator.d.ts +4 -4
  45. package/esm/typings/src/execution/utils/checkExpectations.d.ts +1 -1
  46. package/esm/typings/src/execution/utils/uncertainNumber.d.ts +3 -2
  47. package/esm/typings/src/formats/_common/{FormatDefinition.d.ts → FormatParser.d.ts} +8 -6
  48. package/esm/typings/src/formats/_common/FormatSubvalueParser.d.ts +66 -0
  49. package/esm/typings/src/formats/csv/CsvFormatParser.d.ts +17 -0
  50. package/esm/typings/src/formats/csv/CsvSettings.d.ts +2 -2
  51. package/esm/typings/src/formats/csv/utils/csvParse.d.ts +12 -0
  52. package/esm/typings/src/formats/csv/utils/isValidCsvString.d.ts +1 -1
  53. package/esm/typings/src/formats/index.d.ts +2 -2
  54. package/esm/typings/src/formats/json/{JsonFormatDefinition.d.ts → JsonFormatParser.d.ts} +6 -6
  55. package/esm/typings/src/formats/json/utils/isValidJsonString.d.ts +1 -1
  56. package/esm/typings/src/formats/json/utils/jsonParse.d.ts +8 -0
  57. package/esm/typings/src/formats/text/{TextFormatDefinition.d.ts → TextFormatParser.d.ts} +7 -7
  58. package/esm/typings/src/formats/xml/XmlFormatParser.d.ts +19 -0
  59. package/esm/typings/src/formats/xml/utils/isValidXmlString.d.ts +1 -1
  60. package/esm/typings/src/formfactors/_boilerplate/BoilerplateFormfactorDefinition.d.ts +3 -2
  61. package/esm/typings/src/formfactors/_common/AbstractFormfactorDefinition.d.ts +16 -7
  62. package/esm/typings/src/formfactors/_common/FormfactorDefinition.d.ts +3 -1
  63. package/esm/typings/src/formfactors/_common/string_formfactor_name.d.ts +2 -1
  64. package/esm/typings/src/formfactors/chatbot/ChatbotFormfactorDefinition.d.ts +2 -2
  65. package/esm/typings/src/formfactors/completion/CompletionFormfactorDefinition.d.ts +29 -0
  66. package/esm/typings/src/formfactors/generator/GeneratorFormfactorDefinition.d.ts +2 -1
  67. package/esm/typings/src/formfactors/generic/GenericFormfactorDefinition.d.ts +2 -2
  68. package/esm/typings/src/formfactors/index.d.ts +33 -8
  69. package/esm/typings/src/formfactors/matcher/MatcherFormfactorDefinition.d.ts +4 -2
  70. package/esm/typings/src/formfactors/sheets/SheetsFormfactorDefinition.d.ts +3 -2
  71. package/esm/typings/src/formfactors/translator/TranslatorFormfactorDefinition.d.ts +3 -2
  72. package/esm/typings/src/high-level-abstractions/index.d.ts +2 -2
  73. package/esm/typings/src/llm-providers/_common/register/$llmToolsMetadataRegister.d.ts +3 -3
  74. package/esm/typings/src/llm-providers/_common/register/$llmToolsRegister.d.ts +3 -3
  75. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +4 -4
  76. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +4 -3
  77. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +17 -4
  78. package/esm/typings/src/llm-providers/_common/register/LlmToolsConfiguration.d.ts +11 -4
  79. package/esm/typings/src/llm-providers/_common/register/LlmToolsMetadata.d.ts +27 -5
  80. package/esm/typings/src/llm-providers/_common/register/LlmToolsOptions.d.ts +9 -2
  81. package/esm/typings/src/llm-providers/_common/register/createLlmToolsFromConfiguration.d.ts +12 -3
  82. package/esm/typings/src/llm-providers/_common/utils/cache/CacheItem.d.ts +10 -5
  83. package/esm/typings/src/llm-providers/_common/utils/cache/CacheLlmToolsOptions.d.ts +5 -3
  84. package/esm/typings/src/llm-providers/_common/utils/cache/cacheLlmTools.d.ts +3 -3
  85. package/esm/typings/src/llm-providers/_common/utils/count-total-usage/limitTotalUsage.d.ts +5 -5
  86. package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  87. package/esm/typings/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +4 -0
  88. package/esm/typings/src/llm-providers/deepseek/deepseek-models.d.ts +23 -0
  89. package/esm/typings/src/llm-providers/google/google-models.d.ts +23 -0
  90. package/esm/typings/src/llm-providers/openai/OpenAiExecutionTools.d.ts +4 -0
  91. package/esm/typings/src/llm-providers/openai/openai-models.d.ts +1 -1
  92. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +2 -2
  93. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +2 -2
  94. package/esm/typings/src/migrations/migratePipeline.d.ts +9 -0
  95. package/esm/typings/src/other/templates/getBookTemplates.d.ts +2 -2
  96. package/esm/typings/src/personas/preparePersona.d.ts +1 -1
  97. package/esm/typings/src/pipeline/PipelineInterface/PipelineInterface.d.ts +3 -3
  98. package/esm/typings/src/pipeline/PipelineInterface/constants.d.ts +1 -1
  99. package/esm/typings/src/pipeline/PipelineInterface/getPipelineInterface.d.ts +1 -1
  100. package/esm/typings/src/pipeline/PipelineInterface/isPipelineImplementingInterface.d.ts +5 -4
  101. package/esm/typings/src/pipeline/PipelineInterface/isPipelineInterfacesEqual.d.ts +1 -1
  102. package/esm/typings/src/pipeline/PipelineJson/CommonTaskJson.d.ts +9 -6
  103. package/esm/typings/src/pipeline/PipelineJson/PersonaJson.d.ts +4 -2
  104. package/esm/typings/src/pipeline/PipelineJson/PipelineJson.d.ts +3 -2
  105. package/esm/typings/src/pipeline/PipelineString.d.ts +3 -1
  106. package/esm/typings/src/pipeline/book-notation.d.ts +2 -2
  107. package/esm/typings/src/postprocessing/utils/extractJsonBlock.d.ts +1 -1
  108. package/esm/typings/src/prepare/prepareTasks.d.ts +7 -4
  109. package/esm/typings/src/remote-server/openapi-types.d.ts +348 -6
  110. package/esm/typings/src/remote-server/openapi.d.ts +398 -4
  111. package/esm/typings/src/remote-server/types/RemoteServerOptions.d.ts +2 -1
  112. package/esm/typings/src/scrapers/_boilerplate/BoilerplateScraper.d.ts +3 -3
  113. package/esm/typings/src/scrapers/_boilerplate/createBoilerplateScraper.d.ts +1 -1
  114. package/esm/typings/src/scrapers/_boilerplate/register-metadata.d.ts +1 -1
  115. package/esm/typings/src/scrapers/_common/Converter.d.ts +3 -1
  116. package/esm/typings/src/scrapers/_common/Scraper.d.ts +4 -3
  117. package/esm/typings/src/scrapers/_common/ScraperIntermediateSource.d.ts +4 -2
  118. package/esm/typings/src/scrapers/_common/register/$provideFilesystemForNode.d.ts +2 -1
  119. package/esm/typings/src/scrapers/_common/register/$provideScrapersForBrowser.d.ts +6 -3
  120. package/esm/typings/src/scrapers/_common/register/$provideScrapersForNode.d.ts +3 -5
  121. package/esm/typings/src/scrapers/_common/register/$scrapersMetadataRegister.d.ts +3 -3
  122. package/esm/typings/src/scrapers/_common/register/$scrapersRegister.d.ts +3 -2
  123. package/esm/typings/src/scrapers/_common/register/ScraperAndConverterMetadata.d.ts +8 -5
  124. package/esm/typings/src/scrapers/_common/register/ScraperConstructor.d.ts +2 -1
  125. package/esm/typings/src/scrapers/_common/utils/getScraperIntermediateSource.d.ts +6 -5
  126. package/esm/typings/src/scrapers/_common/utils/makeKnowledgeSourceHandler.d.ts +3 -1
  127. package/esm/typings/src/scrapers/document/createDocumentScraper.d.ts +1 -1
  128. package/esm/typings/src/scrapers/document-legacy/createLegacyDocumentScraper.d.ts +2 -1
  129. package/esm/typings/src/scrapers/markdown/createMarkdownScraper.d.ts +4 -1
  130. package/esm/typings/src/scrapers/markitdown/MarkitdownScraper.d.ts +1 -1
  131. package/esm/typings/src/scrapers/pdf/createPdfScraper.d.ts +2 -1
  132. package/esm/typings/src/scrapers/website/createWebsiteScraper.d.ts +3 -4
  133. package/esm/typings/src/scripting/javascript/postprocessing-functions.d.ts +5 -1
  134. package/esm/typings/src/storage/file-cache-storage/FileCacheStorage.d.ts +12 -5
  135. package/esm/typings/src/storage/file-cache-storage/FileCacheStorageOptions.d.ts +4 -2
  136. package/esm/typings/src/storage/file-cache-storage/utils/nameToSubfolderPath.d.ts +2 -1
  137. package/esm/typings/src/storage/local-storage/getIndexedDbStorage.d.ts +10 -0
  138. package/esm/typings/src/storage/local-storage/utils/makePromptbookStorageFromIndexedDb.d.ts +7 -0
  139. package/esm/typings/src/storage/local-storage/utils/makePromptbookStorageFromWebStorage.d.ts +2 -1
  140. package/esm/typings/src/types/IntermediateFilesStrategy.d.ts +2 -1
  141. package/esm/typings/src/types/ModelVariant.d.ts +5 -5
  142. package/esm/typings/src/types/typeAliases.d.ts +17 -13
  143. package/esm/typings/src/utils/$Register.d.ts +8 -7
  144. package/esm/typings/src/utils/editable/edit-pipeline-string/addPipelineCommand.d.ts +2 -2
  145. package/esm/typings/src/utils/editable/edit-pipeline-string/deflatePipeline.d.ts +4 -1
  146. package/esm/typings/src/utils/editable/utils/isFlatPipeline.d.ts +2 -1
  147. package/esm/typings/src/utils/environment/$getGlobalScope.d.ts +2 -1
  148. package/esm/typings/src/utils/expectation-counters/index.d.ts +1 -1
  149. package/esm/typings/src/utils/markdown/extractAllListItemsFromMarkdown.d.ts +1 -1
  150. package/esm/typings/src/utils/normalization/nameToUriPart.d.ts +4 -4
  151. package/esm/typings/src/utils/normalization/nameToUriParts.d.ts +4 -4
  152. package/esm/typings/src/utils/normalization/normalize-to-kebab-case.d.ts +3 -3
  153. package/esm/typings/src/utils/normalization/normalizeTo_SCREAMING_CASE.d.ts +3 -3
  154. package/esm/typings/src/utils/normalization/normalizeTo_camelCase.d.ts +4 -4
  155. package/esm/typings/src/utils/normalization/normalizeTo_snake_case.d.ts +3 -3
  156. package/esm/typings/src/utils/normalization/removeDiacritics.d.ts +3 -3
  157. package/esm/typings/src/utils/normalization/searchKeywords.d.ts +4 -1
  158. package/esm/typings/src/utils/normalization/titleToName.d.ts +4 -4
  159. package/esm/typings/src/utils/organization/empty_object.d.ts +2 -2
  160. package/esm/typings/src/utils/organization/just_empty_object.d.ts +4 -4
  161. package/esm/typings/src/utils/parameters/mapAvailableToExpectedParameters.d.ts +7 -7
  162. package/esm/typings/src/utils/serialization/clonePipeline.d.ts +4 -3
  163. package/esm/typings/src/utils/serialization/deepClone.d.ts +5 -1
  164. package/esm/typings/src/utils/validators/javascriptName/isValidJavascriptName.d.ts +3 -3
  165. package/esm/typings/src/utils/validators/parameterName/validateParameterName.d.ts +5 -4
  166. package/esm/typings/src/version.d.ts +2 -1
  167. package/package.json +2 -1
  168. package/umd/index.umd.js +1669 -534
  169. package/umd/index.umd.js.map +1 -1
  170. package/esm/typings/src/formats/_common/FormatSubvalueDefinition.d.ts +0 -31
  171. package/esm/typings/src/formats/csv/CsvFormatDefinition.d.ts +0 -17
  172. package/esm/typings/src/formats/xml/XmlFormatDefinition.d.ts +0 -19
package/esm/index.es.js CHANGED
@@ -21,12 +21,13 @@ import { lookup, extension } from 'mime-types';
21
21
  import glob from 'glob-promise';
22
22
  import moment from 'moment';
23
23
  import express from 'express';
24
+ import * as OpenApiValidator from 'express-openapi-validator';
24
25
  import http from 'http';
25
26
  import { Server } from 'socket.io';
26
- import * as OpenApiValidator from 'express-openapi-validator';
27
27
  import swaggerUi from 'swagger-ui-express';
28
28
  import Anthropic from '@anthropic-ai/sdk';
29
29
  import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
30
+ import Bottleneck from 'bottleneck';
30
31
  import OpenAI from 'openai';
31
32
  import { Readability } from '@mozilla/readability';
32
33
  import { JSDOM } from 'jsdom';
@@ -46,7 +47,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
46
47
  * @generated
47
48
  * @see https://github.com/webgptorg/promptbook
48
49
  */
49
- const PROMPTBOOK_ENGINE_VERSION = '0.92.0-3';
50
+ const PROMPTBOOK_ENGINE_VERSION = '0.92.0-31';
50
51
  /**
51
52
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
52
53
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -162,6 +163,21 @@ const DEFAULT_BOOK_OUTPUT_PARAMETER_NAME = 'result';
162
163
  * @public exported from `@promptbook/core`
163
164
  */
164
165
  const DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
166
+ /**
167
+ * Threshold value that determines when a dataset is considered "big"
168
+ * and may require special handling or optimizations
169
+ *
170
+ * For example, when error occurs in one item of the big dataset, it will not fail the whole pipeline
171
+ *
172
+ * @public exported from `@promptbook/core`
173
+ */
174
+ const BIG_DATASET_TRESHOLD = 50;
175
+ /**
176
+ * Placeholder text used to represent a placeholder value of failed operation
177
+ *
178
+ * @public exported from `@promptbook/core`
179
+ */
180
+ const FAILED_VALUE_PLACEHOLDER = '!?';
165
181
  /**
166
182
  * Warning message for the generated sections and files files
167
183
  *
@@ -246,6 +262,7 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [🤹‍♂️]
246
262
  * @public exported from `@promptbook/core`
247
263
  */
248
264
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [🤹‍♂️]
265
+ // <- TODO: [🐝]
249
266
  /**
250
267
  * Where to store your books
251
268
  * This is kind of a "src" for your books
@@ -317,7 +334,7 @@ const MOMENT_ARG_THRESHOLDS = {
317
334
  const DEFAULT_REMOTE_SERVER_URL = REMOTE_SERVER_URLS[0].urls[0];
318
335
  // <- TODO: [🧜‍♂️]
319
336
  /**
320
- * @@@
337
+ * Default settings for parsing and generating CSV files in Promptbook.
321
338
  *
322
339
  * @public exported from `@promptbook/core`
323
340
  */
@@ -328,13 +345,13 @@ const DEFAULT_CSV_SETTINGS = Object.freeze({
328
345
  skipEmptyLines: true,
329
346
  });
330
347
  /**
331
- * @@@
348
+ * Controls whether verbose logging is enabled by default throughout the application.
332
349
  *
333
350
  * @public exported from `@promptbook/core`
334
351
  */
335
352
  let DEFAULT_IS_VERBOSE = false;
336
353
  /**
337
- * @@@
354
+ * Controls whether auto-installation of dependencies is enabled by default.
338
355
  *
339
356
  * @public exported from `@promptbook/core`
340
357
  */
@@ -346,7 +363,15 @@ const DEFAULT_IS_AUTO_INSTALLED = false;
346
363
  */
347
364
  const DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME = `getPipelineCollection`;
348
365
  /**
349
- * @@@
366
+ * Default rate limits (requests per minute)
367
+ *
368
+ * Note: Adjust based on the provider tier you are have
369
+ *
370
+ * @public exported from `@promptbook/core`
371
+ */
372
+ const DEFAULT_MAX_REQUESTS_PER_MINUTE = 60;
373
+ /**
374
+ * Indicates whether pipeline logic validation is enabled. When true, the pipeline logic is checked for consistency.
350
375
  *
351
376
  * @private within the repository
352
377
  */
@@ -686,7 +711,8 @@ class NotYetImplementedError extends Error {
686
711
  }
687
712
 
688
713
  /**
689
- * @@@
714
+ * Safely retrieves the global scope object (window in browser, global in Node.js)
715
+ * regardless of the JavaScript environment in which the code is running
690
716
  *
691
717
  * Note: `$` is used to indicate that this function is not a pure function - it access global scope
692
718
  *
@@ -697,10 +723,10 @@ function $getGlobalScope() {
697
723
  }
698
724
 
699
725
  /**
700
- * @@@
726
+ * Normalizes a text string to SCREAMING_CASE (all uppercase with underscores).
701
727
  *
702
- * @param text @@@
703
- * @returns @@@
728
+ * @param text The text string to be converted to SCREAMING_CASE format.
729
+ * @returns The normalized text in SCREAMING_CASE format.
704
730
  * @example 'HELLO_WORLD'
705
731
  * @example 'I_LOVE_PROMPTBOOK'
706
732
  * @public exported from `@promptbook/utils`
@@ -752,10 +778,10 @@ function normalizeTo_SCREAMING_CASE(text) {
752
778
  */
753
779
 
754
780
  /**
755
- * @@@
781
+ * Normalizes a text string to snake_case format.
756
782
  *
757
- * @param text @@@
758
- * @returns @@@
783
+ * @param text The text string to be converted to snake_case format.
784
+ * @returns The normalized text in snake_case format.
759
785
  * @example 'hello_world'
760
786
  * @example 'i_love_promptbook'
761
787
  * @public exported from `@promptbook/utils`
@@ -765,11 +791,11 @@ function normalizeTo_snake_case(text) {
765
791
  }
766
792
 
767
793
  /**
768
- * Register is @@@
794
+ * Global registry for storing and managing registered entities of a given type.
769
795
  *
770
796
  * Note: `$` is used to indicate that this function is not a pure function - it accesses and adds variables in global scope.
771
797
  *
772
- * @private internal utility, exported are only signleton instances of this class
798
+ * @private internal utility, exported are only singleton instances of this class
773
799
  */
774
800
  class $Register {
775
801
  constructor(registerName) {
@@ -813,10 +839,10 @@ class $Register {
813
839
  }
814
840
 
815
841
  /**
816
- * @@@
842
+ * Register for LLM tools metadata.
817
843
  *
818
844
  * Note: `$` is used to indicate that this interacts with the global scope
819
- * @singleton Only one instance of each register is created per build, but thare can be more @@@
845
+ * @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
820
846
  * @public exported from `@promptbook/core`
821
847
  */
822
848
  const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
@@ -825,10 +851,10 @@ const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
825
851
  */
826
852
 
827
853
  /**
828
- * @@@
854
+ * Register for LLM tools.
829
855
  *
830
856
  * Note: `$` is used to indicate that this interacts with the global scope
831
- * @singleton Only one instance of each register is created per build, but thare can be more @@@
857
+ * @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
832
858
  * @public exported from `@promptbook/core`
833
859
  */
834
860
  const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
@@ -1004,6 +1030,42 @@ function $sideEffect(...sideEffectSubjects) {
1004
1030
  keepUnused(...sideEffectSubjects);
1005
1031
  }
1006
1032
 
1033
+ /**
1034
+ * Converts a JavaScript Object Notation (JSON) string into an object.
1035
+ *
1036
+ * Note: This is wrapper around `JSON.parse()` with better error and type handling
1037
+ *
1038
+ * @public exported from `@promptbook/utils`
1039
+ */
1040
+ function jsonParse(value) {
1041
+ if (value === undefined) {
1042
+ throw new Error(`Can not parse JSON from undefined value.`);
1043
+ }
1044
+ else if (typeof value !== 'string') {
1045
+ console.error('Can not parse JSON from non-string value.', { text: value });
1046
+ throw new Error(spaceTrim(`
1047
+ Can not parse JSON from non-string value.
1048
+
1049
+ The value type: ${typeof value}
1050
+ See more in console.
1051
+ `));
1052
+ }
1053
+ try {
1054
+ return JSON.parse(value);
1055
+ }
1056
+ catch (error) {
1057
+ if (!(error instanceof Error)) {
1058
+ throw error;
1059
+ }
1060
+ throw new Error(spaceTrim((block) => `
1061
+ ${block(error.message)}
1062
+
1063
+ The JSON text:
1064
+ ${block(value)}
1065
+ `));
1066
+ }
1067
+ }
1068
+
1007
1069
  /**
1008
1070
  * Convert identification to Promptbook token
1009
1071
  *
@@ -1052,7 +1114,8 @@ function TODO_USE(...value) {
1052
1114
  }
1053
1115
 
1054
1116
  /**
1055
- * @@@
1117
+ * Provides filesystem access (for example for Node.js-based scrapers)
1118
+ * Creates a standardized filesystem interface that scrapers can use for file operations.
1056
1119
  *
1057
1120
  * @public exported from `@promptbook/node`
1058
1121
  */
@@ -1427,8 +1490,12 @@ function checkSerializableAsJson(options) {
1427
1490
  */
1428
1491
 
1429
1492
  /**
1430
- * @@@
1493
+ * Creates a deep clone of the given object
1431
1494
  *
1495
+ * Note: This method only works for objects that are fully serializable to JSON and do not contain functions, Dates, or special types.
1496
+ *
1497
+ * @param objectValue The object to clone.
1498
+ * @returns A deep, writable clone of the input object.
1432
1499
  * @public exported from `@promptbook/utils`
1433
1500
  */
1434
1501
  function deepClone(objectValue) {
@@ -1483,6 +1550,26 @@ function exportJson(options) {
1483
1550
  * TODO: [🧠] Is there a way how to meaningfully test this utility
1484
1551
  */
1485
1552
 
1553
+ // <- TODO: Maybe do better levels of trust
1554
+ /**
1555
+ * How is the model provider important?
1556
+ *
1557
+ * @public exported from `@promptbook/core`
1558
+ */
1559
+ const MODEL_ORDERS = {
1560
+ /**
1561
+ * Top-tier models, e.g. OpenAI, Anthropic,...
1562
+ */
1563
+ TOP_TIER: 333,
1564
+ /**
1565
+ * Mid-tier models, e.g. Llama, Mistral, etc.
1566
+ */
1567
+ NORMAL: 100,
1568
+ /**
1569
+ * Low-tier models, e.g. Phi, Tiny, etc.
1570
+ */
1571
+ LOW_TIER: 0,
1572
+ };
1486
1573
  /**
1487
1574
  * Order of keys in the pipeline JSON
1488
1575
  *
@@ -1510,13 +1597,19 @@ const ORDER_OF_PIPELINE_JSON = [
1510
1597
  */
1511
1598
  const REPLACING_NONCE = 'ptbkauk42kV2dzao34faw7FudQUHYPtW';
1512
1599
  /**
1513
- * @@@
1600
+ * Nonce which is used as string which is not occurring in normal text
1601
+ *
1602
+ * @private within the repository
1603
+ */
1604
+ const SALT_NONCE = 'ptbkghhewbvruets21t54et5';
1605
+ /**
1606
+ * Placeholder value indicating a parameter is missing its value.
1514
1607
  *
1515
1608
  * @private within the repository
1516
1609
  */
1517
1610
  const RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
1518
1611
  /**
1519
- * @@@
1612
+ * Placeholder value indicating a parameter is restricted and cannot be used directly.
1520
1613
  *
1521
1614
  * @private within the repository
1522
1615
  */
@@ -1951,10 +2044,10 @@ for (let i = 0; i < defaultDiacriticsRemovalMap.length; i++) {
1951
2044
  */
1952
2045
 
1953
2046
  /**
1954
- * @@@
2047
+ * Removes diacritic marks (accents) from characters in a string.
1955
2048
  *
1956
- * @param input @@@
1957
- * @returns @@@
2049
+ * @param input The string containing diacritics to be normalized.
2050
+ * @returns The string with diacritics removed or normalized.
1958
2051
  * @public exported from `@promptbook/utils`
1959
2052
  */
1960
2053
  function removeDiacritics(input) {
@@ -1968,10 +2061,10 @@ function removeDiacritics(input) {
1968
2061
  */
1969
2062
 
1970
2063
  /**
1971
- * @@@
2064
+ * Converts a given text to kebab-case format.
1972
2065
  *
1973
- * @param text @@@
1974
- * @returns @@@
2066
+ * @param text The text to be converted.
2067
+ * @returns The kebab-case formatted string.
1975
2068
  * @example 'hello-world'
1976
2069
  * @example 'i-love-promptbook'
1977
2070
  * @public exported from `@promptbook/utils`
@@ -2019,11 +2112,11 @@ function normalizeToKebabCase(text) {
2019
2112
  */
2020
2113
 
2021
2114
  /**
2022
- * @@@
2115
+ * Converts a title string into a normalized name.
2023
2116
  *
2024
- * @param value @@@
2025
- * @returns @@@
2026
- * @example @@@
2117
+ * @param value The title string to be converted to a name.
2118
+ * @returns A normalized name derived from the input title.
2119
+ * @example 'Hello World!' -> 'hello-world'
2027
2120
  * @public exported from `@promptbook/utils`
2028
2121
  */
2029
2122
  function titleToName(value) {
@@ -2043,7 +2136,8 @@ function titleToName(value) {
2043
2136
  }
2044
2137
 
2045
2138
  /**
2046
- * @@@
2139
+ * Converts a name to a properly formatted subfolder path for cache storage.
2140
+ * Handles normalization and path formatting to create consistent cache directory structures.
2047
2141
  *
2048
2142
  * @private for `FileCacheStorage`
2049
2143
  */
@@ -2052,7 +2146,10 @@ function nameToSubfolderPath(name) {
2052
2146
  }
2053
2147
 
2054
2148
  /**
2055
- * @@@
2149
+ * A storage implementation that caches data in files organized in a directory structure.
2150
+ * Provides methods for retrieving, storing, and managing cached data on the filesystem.
2151
+ *
2152
+ * This class implements the PromptbookStorage interface for filesystem-based caching.
2056
2153
  *
2057
2154
  * @public exported from `@promptbook/node`
2058
2155
  */
@@ -2065,7 +2162,8 @@ class FileCacheStorage {
2065
2162
  }
2066
2163
  }
2067
2164
  /**
2068
- * @@@
2165
+ * Converts a storage key to a filesystem path where the data should be stored.
2166
+ * Creates a consistent, deterministic file path based on the key string.
2069
2167
  */
2070
2168
  getFilenameForKey(key) {
2071
2169
  // TODO: [👬] DRY
@@ -2077,7 +2175,8 @@ class FileCacheStorage {
2077
2175
  ...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${name.substring(0, MAX_FILENAME_LENGTH)}.json`);
2078
2176
  }
2079
2177
  /**
2080
- * @@@ 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.
2178
+ * Returns the current value associated with the given key, or null if the given key does not exist.
2179
+ * Retrieves the cached data from the file system storage.
2081
2180
  */
2082
2181
  async getItem(key) {
2083
2182
  const filename = this.getFilenameForKey(key);
@@ -2085,12 +2184,13 @@ class FileCacheStorage {
2085
2184
  return null;
2086
2185
  }
2087
2186
  const fileContent = await readFile(filename, 'utf-8');
2088
- const value = JSON.parse(fileContent);
2187
+ const value = jsonParse(fileContent);
2089
2188
  // TODO: [🌗]
2090
2189
  return value;
2091
2190
  }
2092
2191
  /**
2093
- * @@@ Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
2192
+ * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
2193
+ * Persists data to the file system, creating necessary directory structure if it doesn't exist.
2094
2194
  */
2095
2195
  async setItem(key, value) {
2096
2196
  const filename = this.getFilenameForKey(key);
@@ -2102,7 +2202,8 @@ class FileCacheStorage {
2102
2202
  await writeFile(filename, fileContent, 'utf-8');
2103
2203
  }
2104
2204
  /**
2105
- * @@@ Removes the key/value pair with the given key from the list associated with the object, if a key/value pair with the given key exists.
2205
+ * Removes the key/value pair with the given key from the storage, if a key/value pair with the given key exists.
2206
+ * Deletes the corresponding file from the filesystem.
2106
2207
  */
2107
2208
  async removeItem(key) {
2108
2209
  const filename = this.getFilenameForKey(key);
@@ -2581,6 +2682,23 @@ class RemoteLlmExecutionTools {
2581
2682
  * TODO: [🧠] Maybe remove `@promptbook/remote-client` and just use `@promptbook/core`
2582
2683
  */
2583
2684
 
2685
+ /**
2686
+ * Parses the task and returns the list of all parameter names
2687
+ *
2688
+ * @param template the string template with parameters in {curly} braces
2689
+ * @returns the list of parameter names
2690
+ * @public exported from `@promptbook/utils`
2691
+ */
2692
+ function extractParameterNames(template) {
2693
+ const matches = template.matchAll(/{\w+}/g);
2694
+ const parameterNames = new Set();
2695
+ for (const match of matches) {
2696
+ const parameterName = match[0].slice(1, -1);
2697
+ parameterNames.add(parameterName);
2698
+ }
2699
+ return parameterNames;
2700
+ }
2701
+
2584
2702
  /**
2585
2703
  * Stores data in memory (HEAP)
2586
2704
  *
@@ -2670,13 +2788,36 @@ function cacheLlmTools(llmTools, options = {}) {
2670
2788
  const callCommonModel = async (prompt) => {
2671
2789
  const { parameters, content, modelRequirements } = prompt;
2672
2790
  // <- Note: These are relevant things from the prompt that the cache key should depend on.
2791
+ // TODO: Maybe some standalone function for normalization of content for cache
2792
+ let normalizedContent = content;
2793
+ normalizedContent = normalizedContent.replace(/\s+/g, ' ');
2794
+ normalizedContent = normalizedContent.split('\r\n').join('\n');
2795
+ normalizedContent = spaceTrim(normalizedContent);
2796
+ // Note: Do not need to save everything in the cache, just the relevant parameters
2797
+ const relevantParameterNames = extractParameterNames(content);
2798
+ const relevantParameters = Object.fromEntries(Object.entries(parameters).filter(([key]) => relevantParameterNames.has(key)));
2799
+ const keyHashBase = { relevantParameters, normalizedContent, modelRequirements };
2673
2800
  const key = titleToName(prompt.title.substring(0, MAX_FILENAME_LENGTH - 10) +
2674
2801
  '-' +
2675
- sha256(hexEncoder.parse(JSON.stringify({ parameters, content, modelRequirements }))).toString( /* hex */));
2802
+ sha256(hexEncoder.parse(JSON.stringify(keyHashBase)))
2803
+ .toString( /* hex */)
2804
+ .substring(0, 10 - 1));
2676
2805
  const cacheItem = !isCacheReloaded ? await storage.getItem(key) : null;
2677
2806
  if (cacheItem) {
2807
+ console.log('!!! Cache hit for key:', { key, keyHashBase });
2678
2808
  return cacheItem.promptResult;
2679
2809
  }
2810
+ console.log('!!! Cache miss for key:', key, {
2811
+ prompt,
2812
+ 'prompt.title': prompt.title,
2813
+ MAX_FILENAME_LENGTH,
2814
+ keyHashBase,
2815
+ parameters,
2816
+ relevantParameters,
2817
+ content,
2818
+ normalizedContent,
2819
+ modelRequirements,
2820
+ });
2680
2821
  let promptResult;
2681
2822
  variant: switch (prompt.modelRequirements.modelVariant) {
2682
2823
  case 'CHAT':
@@ -2697,7 +2838,16 @@ function cacheLlmTools(llmTools, options = {}) {
2697
2838
  await storage.setItem(key, {
2698
2839
  date: $getCurrentDate(),
2699
2840
  promptbookVersion: PROMPTBOOK_ENGINE_VERSION,
2700
- prompt,
2841
+ bookVersion: BOOK_LANGUAGE_VERSION,
2842
+ prompt: {
2843
+ ...prompt,
2844
+ parameters: Object.entries(parameters).length === Object.entries(relevantParameters).length
2845
+ ? parameters
2846
+ : {
2847
+ ...relevantParameters,
2848
+ note: `<- Note: Only relevant parameters are stored in the cache`,
2849
+ },
2850
+ },
2701
2851
  promptResult,
2702
2852
  });
2703
2853
  return promptResult;
@@ -2723,9 +2873,9 @@ function cacheLlmTools(llmTools, options = {}) {
2723
2873
  /**
2724
2874
  * TODO: [🧠][💸] Maybe make some common abstraction `interceptLlmTools` and use here (or use javascript Proxy?)
2725
2875
  * TODO: [🧠] Is there some meaningfull way how to test this util
2726
- * TODO: [👷‍♂️] @@@ Manual about construction of llmTools
2727
- * @@@ write discussion about this and storages
2728
- * @@@ write how to combine multiple interceptors
2876
+ * TODO: [👷‍♂️] Comprehensive manual about construction of llmTools
2877
+ * Detailed explanation about caching strategies and appropriate storage selection for different use cases
2878
+ * Examples of how to combine multiple interceptors for advanced caching, logging, and usage tracking
2729
2879
  */
2730
2880
 
2731
2881
  /**
@@ -2915,9 +3065,8 @@ function countUsage(llmTools) {
2915
3065
  */
2916
3066
 
2917
3067
  /**
2918
- * @@@
3068
+ * Provides LLM tools configuration by reading environment variables.
2919
3069
  *
2920
- * @@@ .env
2921
3070
  * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
2922
3071
  *
2923
3072
  * It looks for environment variables:
@@ -2925,7 +3074,8 @@ function countUsage(llmTools) {
2925
3074
  * - `process.env.ANTHROPIC_CLAUDE_API_KEY`
2926
3075
  * - ...
2927
3076
  *
2928
- * @returns @@@
3077
+ * @see Environment variables documentation or .env file for required variables.
3078
+ * @returns A promise that resolves to the LLM tools configuration, or null if configuration is incomplete or missing.
2929
3079
  * @public exported from `@promptbook/node`
2930
3080
  */
2931
3081
  async function $provideLlmToolsConfigurationFromEnv() {
@@ -3139,11 +3289,16 @@ function joinLlmExecutionTools(...llmExecutionTools) {
3139
3289
  */
3140
3290
 
3141
3291
  /**
3142
- * @@@
3292
+ * Creates LLM execution tools from provided configuration objects
3293
+ *
3294
+ * Instantiates and configures LLM tool instances for each configuration entry,
3295
+ * combining them into a unified interface via MultipleLlmExecutionTools.
3143
3296
  *
3144
3297
  * Note: This function is not cached, every call creates new instance of `MultipleLlmExecutionTools`
3145
3298
  *
3146
- * @returns @@@
3299
+ * @param configuration Array of LLM tool configurations to instantiate
3300
+ * @param options Additional options for configuring the LLM tools
3301
+ * @returns A unified interface combining all successfully instantiated LLM tools
3147
3302
  * @public exported from `@promptbook/core`
3148
3303
  */
3149
3304
  function createLlmToolsFromConfiguration(configuration, options = {}) {
@@ -3182,7 +3337,11 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
3182
3337
  /**
3183
3338
  * TODO: [🎌] Together with `createLlmToolsFromConfiguration` + 'EXECUTION_TOOLS_CLASSES' gets to `@promptbook/core` ALL model providers, make this more efficient
3184
3339
  * TODO: [🧠][🎌] Dynamically install required providers
3185
- * TODO: @@@ write discussion about this - wizzard
3340
+ * TODO: We should implement an interactive configuration wizard that would:
3341
+ * 1. Detect which LLM providers are available in the environment
3342
+ * 2. Guide users through required configuration settings for each provider
3343
+ * 3. Allow testing connections before completing setup
3344
+ * 4. Generate appropriate configuration code for application integration
3186
3345
  * TODO: [🧠][🍛] Which name is better `createLlmToolsFromConfig` or `createLlmToolsFromConfiguration`?
3187
3346
  * TODO: [🧠] Is there some meaningfull way how to test this util
3188
3347
  * TODO: This should be maybe not under `_common` but under `utils`
@@ -3190,11 +3349,14 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
3190
3349
  */
3191
3350
 
3192
3351
  /**
3193
- * @@@
3352
+ * Automatically configures LLM tools from environment variables in Node.js
3353
+ *
3354
+ * This utility function detects available LLM providers based on environment variables
3355
+ * and creates properly configured LLM execution tools for each detected provider.
3194
3356
  *
3195
3357
  * Note: This function is not cached, every call creates new instance of `MultipleLlmExecutionTools`
3196
3358
  *
3197
- * @@@ .env
3359
+ * Supports environment variables from .env files when dotenv is configured
3198
3360
  * Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
3199
3361
  *
3200
3362
  * It looks for environment variables:
@@ -3202,7 +3364,8 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
3202
3364
  * - `process.env.ANTHROPIC_CLAUDE_API_KEY`
3203
3365
  * - ...
3204
3366
  *
3205
- * @returns @@@
3367
+ * @param options Configuration options for the LLM tools
3368
+ * @returns A unified interface containing all detected and configured LLM tools
3206
3369
  * @public exported from `@promptbook/node`
3207
3370
  */
3208
3371
  async function $provideLlmToolsFromEnv(options = {}) {
@@ -3228,7 +3391,16 @@ async function $provideLlmToolsFromEnv(options = {}) {
3228
3391
  return createLlmToolsFromConfiguration(configuration, options);
3229
3392
  }
3230
3393
  /**
3231
- * TODO: @@@ write `$provideLlmToolsFromEnv` vs `$provideLlmToolsConfigurationFromEnv` vs `createLlmToolsFromConfiguration`
3394
+ * TODO: The architecture for LLM tools configuration consists of three key functions:
3395
+ * 1. `$provideLlmToolsFromEnv` - High-level function that detects available providers from env vars and returns ready-to-use LLM tools
3396
+ * 2. `$provideLlmToolsConfigurationFromEnv` - Middle layer that extracts configuration objects from environment variables
3397
+ * 3. `createLlmToolsFromConfiguration` - Low-level function that instantiates LLM tools from explicit configuration
3398
+ *
3399
+ * This layered approach allows flexibility in how tools are configured:
3400
+ * - Use $provideLlmToolsFromEnv for automatic detection and setup in Node.js environments
3401
+ * - Use $provideLlmToolsConfigurationFromEnv to extract config objects for modification before instantiation
3402
+ * - Use createLlmToolsFromConfiguration for explicit control over tool configurations
3403
+ *
3232
3404
  * TODO: [🧠][🍛] Which name is better `$provideLlmToolsFromEnv` or `$provideLlmToolsFromEnvironment`?
3233
3405
  * TODO: [🧠] Is there some meaningfull way how to test this util
3234
3406
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
@@ -3396,15 +3568,19 @@ async function $provideLlmToolsForCli(options) {
3396
3568
  type: 'text',
3397
3569
  name: 'username',
3398
3570
  message: 'Enter your email:',
3399
- validate: (value) => (isValidEmail(value) ? true : 'Valid email is required'),
3571
+ validate(value) {
3572
+ return isValidEmail(value) ? true : 'Valid email is required';
3573
+ },
3400
3574
  },
3401
3575
  {
3402
3576
  type: 'password',
3403
3577
  name: 'password',
3404
3578
  message: 'Enter your password:',
3405
- validate: (value) => value.length /* <- TODO: [🧠] Better password validation */ > 0
3406
- ? true
3407
- : 'Password is required',
3579
+ validate(value) {
3580
+ return value.length /* <- TODO: [🧠] Better password validation */ > 0
3581
+ ? true
3582
+ : 'Password is required';
3583
+ },
3408
3584
  },
3409
3585
  ]);
3410
3586
  const loginUrl = `${remoteServerUrl}/login`;
@@ -3420,7 +3596,7 @@ async function $provideLlmToolsForCli(options) {
3420
3596
  password,
3421
3597
  }),
3422
3598
  });
3423
- const { isSuccess, message, error, identification } = (await response.json());
3599
+ const { isSuccess, message, error, identification } = jsonParse(await response.text());
3424
3600
  if (message) {
3425
3601
  if (isSuccess) {
3426
3602
  console.log(colors.green(message));
@@ -3641,7 +3817,8 @@ function $execCommand(options) {
3641
3817
  */
3642
3818
 
3643
3819
  /**
3644
- * @@@
3820
+ * Attempts to locate the specified application on a Linux system using the 'which' command.
3821
+ * Returns the path to the executable if found, or null otherwise.
3645
3822
  *
3646
3823
  * @private within the repository
3647
3824
  */
@@ -3684,7 +3861,8 @@ async function isExecutable(path, fs) {
3684
3861
  // eslint-disable-next-line @typescript-eslint/no-var-requires
3685
3862
  const userhome = require('userhome');
3686
3863
  /**
3687
- * @@@
3864
+ * Attempts to locate the specified application on a macOS system by checking standard application paths and using mdfind.
3865
+ * Returns the path to the executable if found, or null otherwise.
3688
3866
  *
3689
3867
  * @private within the repository
3690
3868
  */
@@ -3716,7 +3894,8 @@ async function locateAppOnMacOs({ macOsName, }) {
3716
3894
  */
3717
3895
 
3718
3896
  /**
3719
- * @@@
3897
+ * Attempts to locate the specified application on a Windows system by searching common installation directories.
3898
+ * Returns the path to the executable if found, or null otherwise.
3720
3899
  *
3721
3900
  * @private within the repository
3722
3901
  */
@@ -3787,7 +3966,8 @@ function locateApp(options) {
3787
3966
  */
3788
3967
 
3789
3968
  /**
3790
- * @@@
3969
+ * Locates the LibreOffice executable on the current system by searching platform-specific paths.
3970
+ * Returns the path to the executable if found, or null otherwise.
3791
3971
  *
3792
3972
  * @private within the repository
3793
3973
  */
@@ -3805,7 +3985,8 @@ function locateLibreoffice() {
3805
3985
  */
3806
3986
 
3807
3987
  /**
3808
- * @@@
3988
+ * Locates the Pandoc executable on the current system by searching platform-specific paths.
3989
+ * Returns the path to the executable if found, or null otherwise.
3809
3990
  *
3810
3991
  * @private within the repository
3811
3992
  */
@@ -3823,7 +4004,7 @@ function locatePandoc() {
3823
4004
  */
3824
4005
 
3825
4006
  /**
3826
- * @@@
4007
+ * Provides paths to required executables (i.e. as Pandoc and LibreOffice) for Node.js environments.
3827
4008
  *
3828
4009
  * @public exported from `@promptbook/node`
3829
4010
  */
@@ -3843,10 +4024,11 @@ async function $provideExecutablesForNode(options) {
3843
4024
  */
3844
4025
 
3845
4026
  /**
3846
- * @@@
4027
+ * Registry for all available scrapers in the system.
4028
+ * Central point for registering and accessing different types of content scrapers.
3847
4029
  *
3848
4030
  * Note: `$` is used to indicate that this interacts with the global scope
3849
- * @singleton Only one instance of each register is created per build, but thare can be more @@@
4031
+ * @singleton Only one instance of each register is created per build, but there can be more than one in different build modules
3850
4032
  * @public exported from `@promptbook/core`
3851
4033
  */
3852
4034
  const $scrapersRegister = new $Register('scraper_constructors');
@@ -3855,11 +4037,9 @@ const $scrapersRegister = new $Register('scraper_constructors');
3855
4037
  */
3856
4038
 
3857
4039
  /**
3858
- * @@@
3859
- *
3860
- * 1) @@@
3861
- * 2) @@@
3862
- *
4040
+ * Provides a collection of scrapers optimized for Node.js environment.
4041
+ * 1) `provideScrapersForNode` use as default
4042
+ * 2) `provideScrapersForBrowser` use in limited browser environment *
3863
4043
  * @public exported from `@promptbook/node`
3864
4044
  */
3865
4045
  async function $provideScrapersForNode(tools, options) {
@@ -3884,10 +4064,10 @@ async function $provideScrapersForNode(tools, options) {
3884
4064
  */
3885
4065
 
3886
4066
  /**
3887
- * @@@
4067
+ * Global registry for storing metadata about all available scrapers and converters.
3888
4068
  *
3889
- * Note: `$` is used to indicate that this interacts with the global scope
3890
- * @singleton Only one instance of each register is created per build, but thare can be more @@@
4069
+ * Note: `$` is used to indicate that this interacts with the global scope.
4070
+ * @singleton Only one instance of each register is created per build, but there can be more in different contexts (e.g., tests).
3891
4071
  * @public exported from `@promptbook/core`
3892
4072
  */
3893
4073
  const $scrapersMetadataRegister = new $Register('scrapers_metadata');
@@ -4451,7 +4631,7 @@ async function loadArchive(filePath, fs) {
4451
4631
  if (!indexFile) {
4452
4632
  throw new UnexpectedError(`Archive does not contain 'index.book.json' file`);
4453
4633
  }
4454
- const collectionJson = JSON.parse(await indexFile.async('text'));
4634
+ const collectionJson = jsonParse(await indexFile.async('text'));
4455
4635
  for (const pipeline of collectionJson) {
4456
4636
  validatePipeline(pipeline);
4457
4637
  }
@@ -4461,13 +4641,13 @@ async function loadArchive(filePath, fs) {
4461
4641
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
4462
4642
  */
4463
4643
 
4464
- 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"}];
4644
+ 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"}];
4465
4645
 
4466
4646
  /**
4467
4647
  * Function isValidJsonString will tell you if the string is valid JSON or not
4468
4648
  *
4469
4649
  * @param value The string to check
4470
- * @returns True if the string is a valid JSON string, false otherwise
4650
+ * @returns `true` if the string is a valid JSON string, false otherwise
4471
4651
  *
4472
4652
  * @public exported from `@promptbook/utils`
4473
4653
  */
@@ -4697,23 +4877,6 @@ function taskParameterJsonToString(taskParameterJson) {
4697
4877
  * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
4698
4878
  */
4699
4879
 
4700
- /**
4701
- * Parses the task and returns the list of all parameter names
4702
- *
4703
- * @param template the string template with parameters in {curly} braces
4704
- * @returns the list of parameter names
4705
- * @public exported from `@promptbook/utils`
4706
- */
4707
- function extractParameterNames(template) {
4708
- const matches = template.matchAll(/{\w+}/g);
4709
- const parameterNames = new Set();
4710
- for (const match of matches) {
4711
- const parameterName = match[0].slice(1, -1);
4712
- parameterNames.add(parameterName);
4713
- }
4714
- return parameterNames;
4715
- }
4716
-
4717
4880
  /**
4718
4881
  * Unprepare just strips the preparation data of the pipeline
4719
4882
  *
@@ -4722,7 +4885,7 @@ function extractParameterNames(template) {
4722
4885
  */
4723
4886
  function unpreparePipeline(pipeline) {
4724
4887
  let { personas, knowledgeSources, tasks } = pipeline;
4725
- personas = personas.map((persona) => ({ ...persona, modelRequirements: undefined, preparationIds: undefined }));
4888
+ personas = personas.map((persona) => ({ ...persona, modelsRequirements: undefined, preparationIds: undefined }));
4726
4889
  knowledgeSources = knowledgeSources.map((knowledgeSource) => ({ ...knowledgeSource, preparationIds: undefined }));
4727
4890
  tasks = tasks.map((task) => {
4728
4891
  let { dependentParameterNames } = task;
@@ -4763,7 +4926,7 @@ class SimplePipelineCollection {
4763
4926
  /**
4764
4927
  * Constructs a pipeline collection from pipelines
4765
4928
  *
4766
- * @param pipelines @@@
4929
+ * @param pipelines Array of pipeline JSON objects to include in the collection
4767
4930
  *
4768
4931
  * Note: During the construction logic of all pipelines are validated
4769
4932
  * Note: It is not recommended to use this constructor directly, use `createCollectionFromJson` *(or other variant)* instead
@@ -4875,15 +5038,21 @@ function createCollectionFromJson(...promptbooks) {
4875
5038
  * @public exported from `@promptbook/core`
4876
5039
  */
4877
5040
  function isPipelinePrepared(pipeline) {
4878
- // Note: Ignoring `pipeline.preparations` @@@
4879
- // Note: Ignoring `pipeline.knowledgePieces` @@@
5041
+ // Note: Ignoring `pipeline.preparations`
5042
+ // Note: Ignoring `pipeline.knowledgePieces`
4880
5043
  if (pipeline.title === undefined || pipeline.title === '' || pipeline.title === DEFAULT_BOOK_TITLE) {
5044
+ // TODO: !!! Comment this out
5045
+ console.log('Pipeline is not prepared because title is undefined or empty', pipeline);
4881
5046
  return false;
4882
5047
  }
4883
- if (!pipeline.personas.every((persona) => persona.modelRequirements !== undefined)) {
5048
+ if (!pipeline.personas.every((persona) => persona.modelsRequirements !== undefined)) {
5049
+ // TODO: !!! Comment this out
5050
+ console.log('Pipeline is not prepared because personas are not prepared', pipeline.personas);
4884
5051
  return false;
4885
5052
  }
4886
5053
  if (!pipeline.knowledgeSources.every((knowledgeSource) => knowledgeSource.preparationIds !== undefined)) {
5054
+ // TODO: !!! Comment this out
5055
+ console.log('Pipeline is not prepared because knowledge sources are not prepared', pipeline.knowledgeSources);
4887
5056
  return false;
4888
5057
  }
4889
5058
  /*
@@ -4904,6 +5073,35 @@ function isPipelinePrepared(pipeline) {
4904
5073
  * - [♨] Are tasks prepared
4905
5074
  */
4906
5075
 
5076
+ /**
5077
+ * Serializes an error into a [🚉] JSON-serializable object
5078
+ *
5079
+ * @public exported from `@promptbook/utils`
5080
+ */
5081
+ function serializeError(error) {
5082
+ const { name, message, stack } = error;
5083
+ const { id } = error;
5084
+ if (!Object.keys(ALL_ERRORS).includes(name)) {
5085
+ console.error(spaceTrim((block) => `
5086
+
5087
+ Cannot serialize error with name "${name}"
5088
+
5089
+ Authors of Promptbook probably forgot to add this error into the list of errors:
5090
+ https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
5091
+
5092
+
5093
+ ${block(stack || message)}
5094
+
5095
+ `));
5096
+ }
5097
+ return {
5098
+ name: name,
5099
+ message,
5100
+ stack,
5101
+ id, // Include id in the serialized object
5102
+ };
5103
+ }
5104
+
4907
5105
  /**
4908
5106
  * Recursively converts JSON strings to JSON objects
4909
5107
 
@@ -4922,7 +5120,7 @@ function jsonStringsToJsons(object) {
4922
5120
  const newObject = { ...object };
4923
5121
  for (const [key, value] of Object.entries(object)) {
4924
5122
  if (typeof value === 'string' && isValidJsonString(value)) {
4925
- newObject[key] = JSON.parse(value);
5123
+ newObject[key] = jsonParse(value);
4926
5124
  }
4927
5125
  else {
4928
5126
  newObject[key] = jsonStringsToJsons(value);
@@ -5073,35 +5271,6 @@ function createTask(options) {
5073
5271
  * TODO: [🐚] Split into more files and make `PrepareTask` & `RemoteTask` + split the function
5074
5272
  */
5075
5273
 
5076
- /**
5077
- * Serializes an error into a [🚉] JSON-serializable object
5078
- *
5079
- * @public exported from `@promptbook/utils`
5080
- */
5081
- function serializeError(error) {
5082
- const { name, message, stack } = error;
5083
- const { id } = error;
5084
- if (!Object.keys(ALL_ERRORS).includes(name)) {
5085
- console.error(spaceTrim((block) => `
5086
-
5087
- Cannot serialize error with name "${name}"
5088
-
5089
- Authors of Promptbook probably forgot to add this error into the list of errors:
5090
- https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
5091
-
5092
-
5093
- ${block(stack || message)}
5094
-
5095
- `));
5096
- }
5097
- return {
5098
- name: name,
5099
- message,
5100
- stack,
5101
- id, // Include id in the serialized object
5102
- };
5103
- }
5104
-
5105
5274
  /**
5106
5275
  * Format either small or big number
5107
5276
  *
@@ -5344,7 +5513,7 @@ function union(...sets) {
5344
5513
  }
5345
5514
 
5346
5515
  /**
5347
- * @@@
5516
+ * Contains configuration options for parsing and generating CSV files, such as delimiters and quoting rules.
5348
5517
  *
5349
5518
  * @public exported from `@promptbook/core`
5350
5519
  */
@@ -5353,11 +5522,29 @@ const MANDATORY_CSV_SETTINGS = Object.freeze({
5353
5522
  // encoding: 'utf-8',
5354
5523
  });
5355
5524
 
5525
+ /**
5526
+ * Converts a CSV string into an object
5527
+ *
5528
+ * Note: This is wrapper around `papaparse.parse()` with better autohealing
5529
+ *
5530
+ * @private - for now until `@promptbook/csv` is released
5531
+ */
5532
+ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO: Make CSV Schemas */) {
5533
+ settings = { ...settings, ...MANDATORY_CSV_SETTINGS };
5534
+ // Note: Autoheal invalid '\n' characters
5535
+ if (settings.newline && !settings.newline.includes('\r') && value.includes('\r')) {
5536
+ console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
5537
+ value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
5538
+ }
5539
+ const csv = parse(value, settings);
5540
+ return csv;
5541
+ }
5542
+
5356
5543
  /**
5357
5544
  * Function to check if a string is valid CSV
5358
5545
  *
5359
5546
  * @param value The string to check
5360
- * @returns True if the string is a valid CSV string, false otherwise
5547
+ * @returns `true` if the string is a valid CSV string, false otherwise
5361
5548
  *
5362
5549
  * @public exported from `@promptbook/utils`
5363
5550
  */
@@ -5381,7 +5568,7 @@ function isValidCsvString(value) {
5381
5568
  * @public exported from `@promptbook/core`
5382
5569
  * <- TODO: [🏢] Export from package `@promptbook/csv`
5383
5570
  */
5384
- const CsvFormatDefinition = {
5571
+ const CsvFormatParser = {
5385
5572
  formatName: 'CSV',
5386
5573
  aliases: ['SPREADSHEET', 'TABLE'],
5387
5574
  isValid(value, settings, schema) {
@@ -5393,12 +5580,12 @@ const CsvFormatDefinition = {
5393
5580
  heal(value, settings, schema) {
5394
5581
  throw new Error('Not implemented');
5395
5582
  },
5396
- subvalueDefinitions: [
5583
+ subvalueParsers: [
5397
5584
  {
5398
5585
  subvalueName: 'ROW',
5399
- async mapValues(value, outputParameterName, settings, mapCallback) {
5400
- // TODO: [👨🏾‍🤝‍👨🏼] DRY csv parsing
5401
- const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
5586
+ async mapValues(options) {
5587
+ const { value, outputParameterName, settings, mapCallback, onProgress } = options;
5588
+ const csv = csvParse(value, settings);
5402
5589
  if (csv.errors.length !== 0) {
5403
5590
  throw new CsvFormatError(spaceTrim((block) => `
5404
5591
  CSV parsing error
@@ -5413,23 +5600,37 @@ const CsvFormatDefinition = {
5413
5600
  ${block(value)}
5414
5601
  `));
5415
5602
  }
5416
- const mappedData = await Promise.all(csv.data.map(async (row, index) => {
5603
+ const mappedData = [];
5604
+ const length = csv.data.length;
5605
+ for (let index = 0; index < length; index++) {
5606
+ const row = csv.data[index];
5417
5607
  if (row[outputParameterName]) {
5418
5608
  throw new CsvFormatError(`Can not overwrite existing column "${outputParameterName}" in CSV row`);
5419
5609
  }
5420
- return {
5610
+ const mappedRow = {
5421
5611
  ...row,
5422
- [outputParameterName]: await mapCallback(row, index),
5612
+ [outputParameterName]: await mapCallback(row, index, length),
5423
5613
  };
5424
- }));
5614
+ mappedData.push(mappedRow);
5615
+ if (onProgress) {
5616
+ // Note: Report the CSV with all rows mapped so far
5617
+ /*
5618
+ // TODO: [🛕] Report progress with all the rows including the pending ones
5619
+ const progressData = mappedData.map((row, i) =>
5620
+ i > index ? { ...row, [outputParameterName]: PENDING_VALUE_PLACEHOLDER } : row,
5621
+ );
5622
+ */
5623
+ await onProgress(unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
5624
+ }
5625
+ }
5425
5626
  return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
5426
5627
  },
5427
5628
  },
5428
5629
  {
5429
5630
  subvalueName: 'CELL',
5430
- async mapValues(value, outputParameterName, settings, mapCallback) {
5431
- // TODO: [👨🏾‍🤝‍👨🏼] DRY csv parsing
5432
- const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
5631
+ async mapValues(options) {
5632
+ const { value, settings, mapCallback, onProgress } = options;
5633
+ const csv = csvParse(value, settings);
5433
5634
  if (csv.errors.length !== 0) {
5434
5635
  throw new CsvFormatError(spaceTrim((block) => `
5435
5636
  CSV parsing error
@@ -5445,9 +5646,9 @@ const CsvFormatDefinition = {
5445
5646
  `));
5446
5647
  }
5447
5648
  const mappedData = await Promise.all(csv.data.map(async (row, rowIndex) => {
5448
- return /* not await */ Promise.all(Object.entries(row).map(async ([key, value], columnIndex) => {
5649
+ return /* not await */ Promise.all(Object.entries(row).map(async ([key, value], columnIndex, array) => {
5449
5650
  const index = rowIndex * Object.keys(row).length + columnIndex;
5450
- return /* not await */ mapCallback({ [key]: value }, index);
5651
+ return /* not await */ mapCallback({ [key]: value }, index, array.length);
5451
5652
  }));
5452
5653
  }));
5453
5654
  return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
@@ -5456,10 +5657,10 @@ const CsvFormatDefinition = {
5456
5657
  ],
5457
5658
  };
5458
5659
  /**
5459
- * TODO: [🍓] In `CsvFormatDefinition` implement simple `isValid`
5460
- * TODO: [🍓] In `CsvFormatDefinition` implement partial `canBeValid`
5461
- * TODO: [🍓] In `CsvFormatDefinition` implement `heal
5462
- * TODO: [🍓] In `CsvFormatDefinition` implement `subvalueDefinitions`
5660
+ * TODO: [🍓] In `CsvFormatParser` implement simple `isValid`
5661
+ * TODO: [🍓] In `CsvFormatParser` implement partial `canBeValid`
5662
+ * TODO: [🍓] In `CsvFormatParser` implement `heal
5663
+ * TODO: [🍓] In `CsvFormatParser` implement `subvalueParsers`
5463
5664
  * TODO: [🏢] Allow to expect something inside CSV objects and other formats
5464
5665
  */
5465
5666
 
@@ -5468,7 +5669,7 @@ const CsvFormatDefinition = {
5468
5669
  *
5469
5670
  * @private still in development [🏢]
5470
5671
  */
5471
- const JsonFormatDefinition = {
5672
+ const JsonFormatParser = {
5472
5673
  formatName: 'JSON',
5473
5674
  mimeType: 'application/json',
5474
5675
  isValid(value, settings, schema) {
@@ -5480,28 +5681,28 @@ const JsonFormatDefinition = {
5480
5681
  heal(value, settings, schema) {
5481
5682
  throw new Error('Not implemented');
5482
5683
  },
5483
- subvalueDefinitions: [],
5684
+ subvalueParsers: [],
5484
5685
  };
5485
5686
  /**
5486
5687
  * TODO: [🧠] Maybe propper instance of object
5487
5688
  * TODO: [0] Make string_serialized_json
5488
5689
  * TODO: [1] Make type for JSON Settings and Schema
5489
5690
  * TODO: [🧠] What to use for validating JSONs - JSON Schema, ZoD, typescript types/interfaces,...?
5490
- * TODO: [🍓] In `JsonFormatDefinition` implement simple `isValid`
5491
- * TODO: [🍓] In `JsonFormatDefinition` implement partial `canBeValid`
5492
- * TODO: [🍓] In `JsonFormatDefinition` implement `heal
5493
- * TODO: [🍓] In `JsonFormatDefinition` implement `subvalueDefinitions`
5691
+ * TODO: [🍓] In `JsonFormatParser` implement simple `isValid`
5692
+ * TODO: [🍓] In `JsonFormatParser` implement partial `canBeValid`
5693
+ * TODO: [🍓] In `JsonFormatParser` implement `heal
5694
+ * TODO: [🍓] In `JsonFormatParser` implement `subvalueParsers`
5494
5695
  * TODO: [🏢] Allow to expect something inside JSON objects and other formats
5495
5696
  */
5496
5697
 
5497
5698
  /**
5498
5699
  * Definition for any text - this will be always valid
5499
5700
  *
5500
- * Note: This is not useful for validation, but for splitting and mapping with `subvalueDefinitions`
5701
+ * Note: This is not useful for validation, but for splitting and mapping with `subvalueParsers`
5501
5702
  *
5502
5703
  * @public exported from `@promptbook/core`
5503
5704
  */
5504
- const TextFormatDefinition = {
5705
+ const TextFormatParser = {
5505
5706
  formatName: 'TEXT',
5506
5707
  isValid(value) {
5507
5708
  return typeof value === 'string';
@@ -5510,19 +5711,20 @@ const TextFormatDefinition = {
5510
5711
  return typeof partialValue === 'string';
5511
5712
  },
5512
5713
  heal() {
5513
- throw new UnexpectedError('It does not make sense to call `TextFormatDefinition.heal`');
5714
+ throw new UnexpectedError('It does not make sense to call `TextFormatParser.heal`');
5514
5715
  },
5515
- subvalueDefinitions: [
5716
+ subvalueParsers: [
5516
5717
  {
5517
5718
  subvalueName: 'LINE',
5518
- async mapValues(value, outputParameterName, settings, mapCallback) {
5719
+ async mapValues(options) {
5720
+ const { value, mapCallback, onProgress } = options;
5519
5721
  const lines = value.split('\n');
5520
- const mappedLines = await Promise.all(lines.map((lineContent, lineNumber) =>
5722
+ const mappedLines = await Promise.all(lines.map((lineContent, lineNumber, array) =>
5521
5723
  // TODO: [🧠] Maybe option to skip empty line
5522
5724
  /* not await */ mapCallback({
5523
5725
  lineContent,
5524
5726
  // TODO: [🧠] Maybe also put here `lineNumber`
5525
- }, lineNumber)));
5727
+ }, lineNumber, array.length)));
5526
5728
  return mappedLines.join('\n');
5527
5729
  },
5528
5730
  },
@@ -5532,10 +5734,10 @@ const TextFormatDefinition = {
5532
5734
  /**
5533
5735
  * TODO: [1] Make type for XML Text and Schema
5534
5736
  * TODO: [🧠][🤠] Here should be all words, characters, lines, paragraphs, pages available as subvalues
5535
- * TODO: [🍓] In `TextFormatDefinition` implement simple `isValid`
5536
- * TODO: [🍓] In `TextFormatDefinition` implement partial `canBeValid`
5537
- * TODO: [🍓] In `TextFormatDefinition` implement `heal
5538
- * TODO: [🍓] In `TextFormatDefinition` implement `subvalueDefinitions`
5737
+ * TODO: [🍓] In `TextFormatParser` implement simple `isValid`
5738
+ * TODO: [🍓] In `TextFormatParser` implement partial `canBeValid`
5739
+ * TODO: [🍓] In `TextFormatParser` implement `heal
5740
+ * TODO: [🍓] In `TextFormatParser` implement `subvalueParsers`
5539
5741
  * TODO: [🏢] Allow to expect something inside each item of list and other formats
5540
5742
  */
5541
5743
 
@@ -5543,7 +5745,7 @@ const TextFormatDefinition = {
5543
5745
  * Function to check if a string is valid XML
5544
5746
  *
5545
5747
  * @param value
5546
- * @returns True if the string is a valid XML string, false otherwise
5748
+ * @returns `true` if the string is a valid XML string, false otherwise
5547
5749
  *
5548
5750
  * @public exported from `@promptbook/utils`
5549
5751
  */
@@ -5568,7 +5770,7 @@ function isValidXmlString(value) {
5568
5770
  *
5569
5771
  * @private still in development [🏢]
5570
5772
  */
5571
- const XmlFormatDefinition = {
5773
+ const XmlFormatParser = {
5572
5774
  formatName: 'XML',
5573
5775
  mimeType: 'application/xml',
5574
5776
  isValid(value, settings, schema) {
@@ -5580,17 +5782,17 @@ const XmlFormatDefinition = {
5580
5782
  heal(value, settings, schema) {
5581
5783
  throw new Error('Not implemented');
5582
5784
  },
5583
- subvalueDefinitions: [],
5785
+ subvalueParsers: [],
5584
5786
  };
5585
5787
  /**
5586
5788
  * TODO: [🧠] Maybe propper instance of object
5587
5789
  * TODO: [0] Make string_serialized_xml
5588
5790
  * TODO: [1] Make type for XML Settings and Schema
5589
5791
  * TODO: [🧠] What to use for validating XMLs - XSD,...
5590
- * TODO: [🍓] In `XmlFormatDefinition` implement simple `isValid`
5591
- * TODO: [🍓] In `XmlFormatDefinition` implement partial `canBeValid`
5592
- * TODO: [🍓] In `XmlFormatDefinition` implement `heal
5593
- * TODO: [🍓] In `XmlFormatDefinition` implement `subvalueDefinitions`
5792
+ * TODO: [🍓] In `XmlFormatParser` implement simple `isValid`
5793
+ * TODO: [🍓] In `XmlFormatParser` implement partial `canBeValid`
5794
+ * TODO: [🍓] In `XmlFormatParser` implement `heal
5795
+ * TODO: [🍓] In `XmlFormatParser` implement `subvalueParsers`
5594
5796
  * TODO: [🏢] Allow to expect something inside XML and other formats
5595
5797
  */
5596
5798
 
@@ -5599,24 +5801,19 @@ const XmlFormatDefinition = {
5599
5801
  *
5600
5802
  * @private internal index of `...` <- TODO [🏢]
5601
5803
  */
5602
- const FORMAT_DEFINITIONS = [
5603
- JsonFormatDefinition,
5604
- XmlFormatDefinition,
5605
- TextFormatDefinition,
5606
- CsvFormatDefinition,
5607
- ];
5804
+ const FORMAT_DEFINITIONS = [JsonFormatParser, XmlFormatParser, TextFormatParser, CsvFormatParser];
5608
5805
  /**
5609
5806
  * Note: [💞] Ignore a discrepancy between file name and entity name
5610
5807
  */
5611
5808
 
5612
5809
  /**
5613
- * Maps available parameters to expected parameters
5810
+ * Maps available parameters to expected parameters for a pipeline task.
5614
5811
  *
5615
5812
  * The strategy is:
5616
- * 1) @@@
5617
- * 2) @@@
5813
+ * 1) First, match parameters by name where both available and expected.
5814
+ * 2) Then, if there are unmatched expected and available parameters, map them by order.
5618
5815
  *
5619
- * @throws {PipelineExecutionError} @@@
5816
+ * @throws {PipelineExecutionError} If the number of unmatched expected and available parameters does not match, or mapping is ambiguous.
5620
5817
  * @private within the repository used in `createPipelineExecutor`
5621
5818
  */
5622
5819
  function mapAvailableToExpectedParameters(options) {
@@ -5639,7 +5836,7 @@ function mapAvailableToExpectedParameters(options) {
5639
5836
  else if (!availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) ;
5640
5837
  }
5641
5838
  if (expectedParameterNames.size === 0) {
5642
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
5839
+ // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent accidental modifications after mapping
5643
5840
  Object.freeze(mappedParameters);
5644
5841
  return mappedParameters;
5645
5842
  }
@@ -5670,7 +5867,7 @@ function mapAvailableToExpectedParameters(options) {
5670
5867
  for (let i = 0; i < expectedParameterNames.size; i++) {
5671
5868
  mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
5672
5869
  }
5673
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
5870
+ // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent accidental modifications after mapping
5674
5871
  Object.freeze(mappedParameters);
5675
5872
  return mappedParameters;
5676
5873
  }
@@ -5774,7 +5971,7 @@ function extractJsonBlock(markdown) {
5774
5971
  }
5775
5972
  /**
5776
5973
  * TODO: Add some auto-healing logic + extract YAML, JSON5, TOML, etc.
5777
- * TODO: [🏢] Make this logic part of `JsonFormatDefinition` or `isValidJsonString`
5974
+ * TODO: [🏢] Make this logic part of `JsonFormatParser` or `isValidJsonString`
5778
5975
  */
5779
5976
 
5780
5977
  /**
@@ -5836,10 +6033,12 @@ function templateParameters(template, parameters) {
5836
6033
  throw new PipelineExecutionError('Parameter is already opened or not closed');
5837
6034
  }
5838
6035
  if (parameters[parameterName] === undefined) {
6036
+ console.log('!!! templateParameters 1', { parameterName, template, parameters });
5839
6037
  throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
5840
6038
  }
5841
6039
  let parameterValue = parameters[parameterName];
5842
6040
  if (parameterValue === undefined) {
6041
+ console.log('!!! templateParameters 2', { parameterName, template, parameters });
5843
6042
  throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
5844
6043
  }
5845
6044
  parameterValue = valueToString(parameterValue);
@@ -5995,7 +6194,7 @@ const CountUtils = {
5995
6194
  PAGES: countPages,
5996
6195
  };
5997
6196
  /**
5998
- * TODO: [🧠][🤠] This should be probbably as part of `TextFormatDefinition`
6197
+ * TODO: [🧠][🤠] This should be probbably as part of `TextFormatParser`
5999
6198
  * Note: [💞] Ignore a discrepancy between file name and entity name
6000
6199
  */
6001
6200
 
@@ -6023,13 +6222,17 @@ function checkExpectations(expectations, value) {
6023
6222
  }
6024
6223
  /**
6025
6224
  * TODO: [💝] Unite object for expecting amount and format
6026
- * TODO: [🧠][🤠] This should be part of `TextFormatDefinition`
6225
+ * TODO: [🧠][🤠] This should be part of `TextFormatParser`
6027
6226
  * Note: [💝] and [🤠] are interconnected together
6028
6227
  */
6029
6228
 
6030
6229
  /**
6031
- * @@@
6230
+ * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
6231
+ * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
6232
+ * Throws errors if execution fails after all attempts.
6032
6233
  *
6234
+ * @param options - The options for execution, including task, parameters, pipeline, and configuration.
6235
+ * @returns The result string of the executed task.
6033
6236
  * @private internal utility of `createPipelineExecutor`
6034
6237
  */
6035
6238
  async function executeAttempts(options) {
@@ -6251,7 +6454,7 @@ async function executeAttempts(options) {
6251
6454
  if (task.format) {
6252
6455
  if (task.format === 'JSON') {
6253
6456
  if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
6254
- // TODO: [🏢] Do more universally via `FormatDefinition`
6457
+ // TODO: [🏢] Do more universally via `FormatParser`
6255
6458
  try {
6256
6459
  $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
6257
6460
  }
@@ -6353,12 +6556,16 @@ async function executeAttempts(options) {
6353
6556
  */
6354
6557
 
6355
6558
  /**
6356
- * @@@
6559
+ * Executes a pipeline task that requires mapping or iterating over subvalues of a parameter (such as rows in a CSV).
6560
+ * Handles format and subformat resolution, error handling, and progress reporting.
6561
+ *
6562
+ * @param options - Options for execution, including task details and progress callback.
6563
+ * @returns The result of the subvalue mapping or execution attempts.
6357
6564
  *
6358
6565
  * @private internal utility of `createPipelineExecutor`
6359
6566
  */
6360
6567
  async function executeFormatSubvalues(options) {
6361
- const { task, jokerParameterNames, parameters, priority, csvSettings, pipelineIdentification } = options;
6568
+ const { task, jokerParameterNames, parameters, priority, csvSettings, onProgress, pipelineIdentification } = options;
6362
6569
  if (task.foreach === undefined) {
6363
6570
  return /* not await */ executeAttempts(options);
6364
6571
  }
@@ -6389,16 +6596,16 @@ async function executeFormatSubvalues(options) {
6389
6596
  ${block(pipelineIdentification)}
6390
6597
  `));
6391
6598
  }
6392
- const subvalueDefinition = formatDefinition.subvalueDefinitions.find((subvalueDefinition) => [subvalueDefinition.subvalueName, ...(subvalueDefinition.aliases || [])].includes(task.foreach.subformatName));
6393
- if (subvalueDefinition === undefined) {
6599
+ const subvalueParser = formatDefinition.subvalueParsers.find((subvalueParser) => [subvalueParser.subvalueName, ...(subvalueParser.aliases || [])].includes(task.foreach.subformatName));
6600
+ if (subvalueParser === undefined) {
6394
6601
  throw new UnexpectedError(
6395
6602
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
6396
6603
  spaceTrim((block) => `
6397
6604
  Unsupported subformat name "${task.foreach.subformatName}" for format "${task.foreach.formatName}"
6398
6605
 
6399
6606
  Available subformat names for format "${formatDefinition.formatName}":
6400
- ${block(formatDefinition.subvalueDefinitions
6401
- .map((subvalueDefinition) => subvalueDefinition.subvalueName)
6607
+ ${block(formatDefinition.subvalueParsers
6608
+ .map((subvalueParser) => subvalueParser.subvalueName)
6402
6609
  .map((subvalueName) => `- ${subvalueName}`)
6403
6610
  .join('\n'))}
6404
6611
 
@@ -6412,53 +6619,83 @@ async function executeFormatSubvalues(options) {
6412
6619
  formatSettings = csvSettings;
6413
6620
  // <- TODO: [🤹‍♂️] More universal, make simmilar pattern for other formats for example \n vs \r\n in text
6414
6621
  }
6415
- const resultString = await subvalueDefinition.mapValues(parameterValue, task.foreach.outputSubparameterName, formatSettings, async (subparameters, index) => {
6416
- let mappedParameters;
6417
- // TODO: [🤹‍♂️][🪂] Limit to N concurrent executions
6418
- // TODO: When done [🐚] Report progress also for each subvalue here
6419
- try {
6420
- mappedParameters = mapAvailableToExpectedParameters({
6421
- expectedParameters: Object.fromEntries(task.foreach.inputSubparameterNames.map((subparameterName) => [subparameterName, null])),
6422
- availableParameters: subparameters,
6423
- });
6424
- }
6425
- catch (error) {
6426
- if (!(error instanceof PipelineExecutionError)) {
6427
- throw error;
6622
+ const resultString = await subvalueParser.mapValues({
6623
+ value: parameterValue,
6624
+ outputParameterName: task.foreach.outputSubparameterName,
6625
+ settings: formatSettings,
6626
+ onProgress(partialResultString) {
6627
+ return onProgress(Object.freeze({
6628
+ [task.resultingParameterName]: partialResultString,
6629
+ }));
6630
+ },
6631
+ async mapCallback(subparameters, index, length) {
6632
+ let mappedParameters;
6633
+ try {
6634
+ mappedParameters = mapAvailableToExpectedParameters({
6635
+ expectedParameters: Object.fromEntries(task.foreach.inputSubparameterNames.map((subparameterName) => [subparameterName, null])),
6636
+ availableParameters: subparameters,
6637
+ });
6428
6638
  }
6429
- throw new PipelineExecutionError(spaceTrim((block) => `
6430
- ${error.message}
6639
+ catch (error) {
6640
+ if (!(error instanceof PipelineExecutionError)) {
6641
+ throw error;
6642
+ }
6643
+ const highLevelError = new PipelineExecutionError(spaceTrim((block) => `
6644
+ ${error.message}
6431
6645
 
6432
- This is error in FOREACH command
6433
- You have probbably passed wrong data to pipeline or wrong data was generated which are processed by FOREACH command
6646
+ This is error in FOREACH command when mapping ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
6647
+ You have probbably passed wrong data to pipeline or wrong data was generated which are processed by FOREACH command
6434
6648
 
6435
- ${block(pipelineIdentification)}
6436
- Subparameter index: ${index}
6437
- `));
6438
- }
6439
- const allSubparameters = {
6440
- ...parameters,
6441
- ...mappedParameters,
6442
- };
6443
- // Note: [👨‍👨‍👧] Now we can freeze `subparameters` because we are sure that all and only used parameters are defined and are not going to be changed
6444
- Object.freeze(allSubparameters);
6445
- const subresultString = await executeAttempts({
6446
- ...options,
6447
- priority: priority + index,
6448
- parameters: allSubparameters,
6449
- pipelineIdentification: spaceTrim((block) => `
6450
- ${block(pipelineIdentification)}
6451
- Subparameter index: ${index}
6452
- `),
6453
- });
6454
- return subresultString;
6649
+ ${block(pipelineIdentification)}
6650
+ `));
6651
+ if (length > BIG_DATASET_TRESHOLD) {
6652
+ console.error(highLevelError);
6653
+ return FAILED_VALUE_PLACEHOLDER;
6654
+ }
6655
+ throw highLevelError;
6656
+ }
6657
+ const allSubparameters = {
6658
+ ...parameters,
6659
+ ...mappedParameters,
6660
+ };
6661
+ Object.freeze(allSubparameters);
6662
+ try {
6663
+ const subresultString = await executeAttempts({
6664
+ ...options,
6665
+ priority: priority + index,
6666
+ parameters: allSubparameters,
6667
+ pipelineIdentification: spaceTrim((block) => `
6668
+ ${block(pipelineIdentification)}
6669
+ Subparameter index: ${index}
6670
+ `),
6671
+ });
6672
+ return subresultString;
6673
+ }
6674
+ catch (error) {
6675
+ if (length > BIG_DATASET_TRESHOLD) {
6676
+ console.error(spaceTrim((block) => `
6677
+ ${error.message}
6678
+
6679
+ This is error in FOREACH command when processing ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
6680
+
6681
+ ${block(pipelineIdentification)}
6682
+ `));
6683
+ return FAILED_VALUE_PLACEHOLDER;
6684
+ }
6685
+ throw error;
6686
+ }
6687
+ },
6455
6688
  });
6456
6689
  return resultString;
6457
6690
  }
6458
6691
 
6459
6692
  /**
6460
- * @@@
6693
+ * Returns the context for a given task, typically used to provide additional information or variables
6694
+ * required for the execution of the task within a pipeline. The context is returned as a string value
6695
+ * that may include markdown formatting.
6461
6696
  *
6697
+ * @param task - The task for which the context is being generated. This should be a deeply immutable TaskJson object.
6698
+ * @returns The context as a string, formatted as markdown and parameter value.
6462
6699
  * @private internal utility of `createPipelineExecutor`
6463
6700
  */
6464
6701
  async function getContextForTask(task) {
@@ -6466,7 +6703,7 @@ async function getContextForTask(task) {
6466
6703
  }
6467
6704
 
6468
6705
  /**
6469
- * @@@
6706
+ * Retrieves example values or templates for a given task, used to guide or validate pipeline execution.
6470
6707
  *
6471
6708
  * @private internal utility of `createPipelineExecutor`
6472
6709
  */
@@ -6475,67 +6712,167 @@ async function getExamplesForTask(task) {
6475
6712
  }
6476
6713
 
6477
6714
  /**
6478
- * @@@
6715
+ * Computes the cosine similarity between two embedding vectors
6479
6716
  *
6480
- * @private internal utility of `createPipelineExecutor`
6717
+ * Note: This is helping function for RAG (retrieval-augmented generation)
6718
+ *
6719
+ * @param embeddingVector1
6720
+ * @param embeddingVector2
6721
+ * @returns Cosine similarity between the two vectors
6722
+ *
6723
+ * @public exported from `@promptbook/core`
6481
6724
  */
6482
- async function getKnowledgeForTask(options) {
6483
- const { preparedPipeline, task } = options;
6484
- return preparedPipeline.knowledgePieces.map(({ content }) => `- ${content}`).join('\n');
6485
- // <- TODO: [🧠] Some smart aggregation of knowledge pieces, single-line vs multi-line vs mixed
6725
+ function computeCosineSimilarity(embeddingVector1, embeddingVector2) {
6726
+ if (embeddingVector1.length !== embeddingVector2.length) {
6727
+ throw new TypeError('Embedding vectors must have the same length');
6728
+ }
6729
+ const dotProduct = embeddingVector1.reduce((sum, value, index) => sum + value * embeddingVector2[index], 0);
6730
+ const magnitude1 = Math.sqrt(embeddingVector1.reduce((sum, value) => sum + value * value, 0));
6731
+ const magnitude2 = Math.sqrt(embeddingVector2.reduce((sum, value) => sum + value * value, 0));
6732
+ return 1 - dotProduct / (magnitude1 * magnitude2);
6486
6733
  }
6487
6734
 
6488
6735
  /**
6489
- * @@@
6736
+ *
6737
+ * @param knowledgePieces
6738
+ * @returns
6490
6739
  *
6491
6740
  * @private internal utility of `createPipelineExecutor`
6492
6741
  */
6493
- async function getReservedParametersForTask(options) {
6494
- const { preparedPipeline, task, pipelineIdentification } = options;
6495
- const context = await getContextForTask(); // <- [🏍]
6496
- const knowledge = await getKnowledgeForTask({ preparedPipeline, task });
6497
- const examples = await getExamplesForTask();
6498
- const currentDate = new Date().toISOString(); // <- TODO: [🧠][💩] Better
6499
- const modelName = RESERVED_PARAMETER_MISSING_VALUE;
6500
- const reservedParameters = {
6501
- content: RESERVED_PARAMETER_RESTRICTED,
6502
- context,
6503
- knowledge,
6504
- examples,
6505
- currentDate,
6506
- modelName,
6507
- };
6508
- // Note: Doublecheck that ALL reserved parameters are defined:
6509
- for (const parameterName of RESERVED_PARAMETER_NAMES) {
6510
- if (reservedParameters[parameterName] === undefined) {
6511
- throw new UnexpectedError(spaceTrim$1((block) => `
6512
- Reserved parameter {${parameterName}} is not defined
6513
-
6514
- ${block(pipelineIdentification)}
6515
- `));
6516
- }
6517
- }
6518
- return reservedParameters;
6742
+ function knowledgePiecesToString(knowledgePieces) {
6743
+ return knowledgePieces
6744
+ .map((knowledgePiece) => {
6745
+ const { content } = knowledgePiece;
6746
+ return `- ${content}`;
6747
+ })
6748
+ .join('\n');
6749
+ // <- TODO: [🧠] Some smarter aggregation of knowledge pieces, single-line vs multi-line vs mixed
6519
6750
  }
6520
6751
 
6521
6752
  /**
6522
- * @@@
6753
+ * Retrieves the most relevant knowledge pieces for a given task using embedding-based similarity search.
6754
+ * This is where retrieval-augmented generation (RAG) is performed to enhance the task with external knowledge.
6523
6755
  *
6524
6756
  * @private internal utility of `createPipelineExecutor`
6525
6757
  */
6526
- async function executeTask(options) {
6527
- const { currentTask, preparedPipeline, parametersToPass, tools, onProgress, $executionReport, pipelineIdentification, maxExecutionAttempts, maxParallelCount, csvSettings, isVerbose, rootDirname, cacheDirname, intermediateFilesStrategy, isAutoInstalled, isNotPreparedWarningSupressed, } = options;
6528
- const priority = preparedPipeline.tasks.length - preparedPipeline.tasks.indexOf(currentTask);
6529
- await onProgress({
6530
- outputParameters: {
6531
- [currentTask.resultingParameterName]: '', // <- TODO: [🧠] What is the best value here?
6532
- },
6533
- });
6758
+ async function getKnowledgeForTask(options) {
6759
+ const { tools, preparedPipeline, task, parameters } = options;
6760
+ const firstKnowlegePiece = preparedPipeline.knowledgePieces[0];
6761
+ const firstKnowlegeIndex = firstKnowlegePiece === null || firstKnowlegePiece === void 0 ? void 0 : firstKnowlegePiece.index[0];
6762
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model, use also keyword search
6763
+ if (firstKnowlegePiece === undefined || firstKnowlegeIndex === undefined) {
6764
+ return ''; // <- Note: Np knowledge present, return empty string
6765
+ }
6766
+ try {
6767
+ // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
6768
+ const _llms = arrayableToArray(tools.llm);
6769
+ const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
6770
+ const taskEmbeddingPrompt = {
6771
+ title: 'Knowledge Search',
6772
+ modelRequirements: {
6773
+ modelVariant: 'EMBEDDING',
6774
+ modelName: firstKnowlegeIndex.modelName,
6775
+ },
6776
+ content: task.content,
6777
+ parameters,
6778
+ };
6779
+ const taskEmbeddingResult = await llmTools.callEmbeddingModel(taskEmbeddingPrompt);
6780
+ const knowledgePiecesWithRelevance = preparedPipeline.knowledgePieces.map((knowledgePiece) => {
6781
+ const { index } = knowledgePiece;
6782
+ const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowlegeIndex.modelName);
6783
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model
6784
+ if (knowledgePieceIndex === undefined) {
6785
+ return {
6786
+ content: knowledgePiece.content,
6787
+ relevance: 0,
6788
+ };
6789
+ }
6790
+ const relevance = computeCosineSimilarity(knowledgePieceIndex.position, taskEmbeddingResult.content);
6791
+ return {
6792
+ content: knowledgePiece.content,
6793
+ relevance,
6794
+ };
6795
+ });
6796
+ const knowledgePiecesSorted = knowledgePiecesWithRelevance.sort((a, b) => a.relevance - b.relevance);
6797
+ const knowledgePiecesLimited = knowledgePiecesSorted.slice(0, 5);
6798
+ console.log('!!! Embedding', {
6799
+ task,
6800
+ taskEmbeddingPrompt,
6801
+ taskEmbeddingResult,
6802
+ firstKnowlegePiece,
6803
+ firstKnowlegeIndex,
6804
+ knowledgePiecesWithRelevance,
6805
+ knowledgePiecesSorted,
6806
+ knowledgePiecesLimited,
6807
+ });
6808
+ return knowledgePiecesToString(knowledgePiecesLimited);
6809
+ }
6810
+ catch (error) {
6811
+ assertsError(error);
6812
+ console.error('Error in `getKnowledgeForTask`', error);
6813
+ // Note: If the LLM fails, just return all knowledge pieces
6814
+ return knowledgePiecesToString(preparedPipeline.knowledgePieces);
6815
+ }
6816
+ }
6817
+ /**
6818
+ * TODO: !!!! Verify if this is working
6819
+ * TODO: [♨] Implement Better - use keyword search
6820
+ * TODO: [♨] Examples of values
6821
+ */
6822
+
6823
+ /**
6824
+ * Retrieves all reserved parameters for a given pipeline task, including context, knowledge, examples, and metadata.
6825
+ * Ensures all reserved parameters are defined and throws if any are missing.
6826
+ *
6827
+ * @param options - Options including tools, pipeline, task, and context.
6828
+ * @returns An object containing all reserved parameters for the task.
6829
+ *
6830
+ * @private internal utility of `createPipelineExecutor`
6831
+ */
6832
+ async function getReservedParametersForTask(options) {
6833
+ const { tools, preparedPipeline, task, parameters, pipelineIdentification } = options;
6834
+ const context = await getContextForTask(); // <- [🏍]
6835
+ const knowledge = await getKnowledgeForTask({ tools, preparedPipeline, task, parameters });
6836
+ const examples = await getExamplesForTask();
6837
+ const currentDate = new Date().toISOString(); // <- TODO: [🧠][💩] Better
6838
+ const modelName = RESERVED_PARAMETER_MISSING_VALUE;
6839
+ const reservedParameters = {
6840
+ content: RESERVED_PARAMETER_RESTRICTED,
6841
+ context,
6842
+ knowledge,
6843
+ examples,
6844
+ currentDate,
6845
+ modelName,
6846
+ };
6847
+ // Note: Doublecheck that ALL reserved parameters are defined:
6848
+ for (const parameterName of RESERVED_PARAMETER_NAMES) {
6849
+ if (reservedParameters[parameterName] === undefined) {
6850
+ throw new UnexpectedError(spaceTrim$1((block) => `
6851
+ Reserved parameter {${parameterName}} is not defined
6852
+
6853
+ ${block(pipelineIdentification)}
6854
+ `));
6855
+ }
6856
+ }
6857
+ return reservedParameters;
6858
+ }
6859
+
6860
+ /**
6861
+ * Executes a single task within a pipeline, handling parameter validation, error checking, and progress reporting.
6862
+ *
6863
+ * @param options - Options for execution, including the task, pipeline, parameters, and callbacks.
6864
+ * @returns The output parameters produced by the task.
6865
+ *
6866
+ * @private internal utility of `createPipelineExecutor`
6867
+ */
6868
+ async function executeTask(options) {
6869
+ const { currentTask, preparedPipeline, parametersToPass, tools, onProgress, $executionReport, pipelineIdentification, maxExecutionAttempts, maxParallelCount, csvSettings, isVerbose, rootDirname, cacheDirname, intermediateFilesStrategy, isAutoInstalled, isNotPreparedWarningSupressed, } = options;
6870
+ const priority = preparedPipeline.tasks.length - preparedPipeline.tasks.indexOf(currentTask);
6534
6871
  // Note: Check consistency of used and dependent parameters which was also done in `validatePipeline`, but it’s good to doublecheck
6535
6872
  const usedParameterNames = extractParameterNamesFromTask(currentTask);
6536
6873
  const dependentParameterNames = new Set(currentTask.dependentParameterNames);
6537
6874
  // TODO: [👩🏾‍🤝‍👩🏻] Use here `mapAvailableToExpectedParameters`
6538
- if (union(difference(usedParameterNames, dependentParameterNames), difference(dependentParameterNames, usedParameterNames)).size !== 0) {
6875
+ if (difference(union(difference(usedParameterNames, dependentParameterNames), difference(dependentParameterNames, usedParameterNames)), new Set(RESERVED_PARAMETER_NAMES)).size !== 0) {
6539
6876
  throw new UnexpectedError(spaceTrim$1((block) => `
6540
6877
  Dependent parameters are not consistent with used parameters:
6541
6878
 
@@ -6555,9 +6892,11 @@ async function executeTask(options) {
6555
6892
  }
6556
6893
  const definedParameters = Object.freeze({
6557
6894
  ...(await getReservedParametersForTask({
6895
+ tools,
6558
6896
  preparedPipeline,
6559
6897
  task: currentTask,
6560
6898
  pipelineIdentification,
6899
+ parameters: parametersToPass,
6561
6900
  })),
6562
6901
  ...parametersToPass,
6563
6902
  });
@@ -6603,6 +6942,7 @@ async function executeTask(options) {
6603
6942
  preparedPipeline,
6604
6943
  tools,
6605
6944
  $executionReport,
6945
+ onProgress,
6606
6946
  pipelineIdentification,
6607
6947
  maxExecutionAttempts,
6608
6948
  maxParallelCount,
@@ -6630,7 +6970,8 @@ async function executeTask(options) {
6630
6970
  */
6631
6971
 
6632
6972
  /**
6633
- * @@@
6973
+ * Filters and returns only the output parameters from the provided pipeline execution options.
6974
+ * Adds warnings for any expected output parameters that are missing.
6634
6975
  *
6635
6976
  * @private internal utility of `createPipelineExecutor`
6636
6977
  */
@@ -6655,9 +6996,12 @@ function filterJustOutputParameters(options) {
6655
6996
  }
6656
6997
 
6657
6998
  /**
6658
- * @@@
6999
+ * Executes an entire pipeline, resolving tasks in dependency order, handling errors, and reporting progress.
7000
+ *
7001
+ * Note: This is not a `PipelineExecutor` (which is bound to a single pipeline), but a utility function used by `createPipelineExecutor` to create a `PipelineExecutor`.
6659
7002
  *
6660
- * Note: This is not a `PipelineExecutor` (which is binded with one exact pipeline), but a utility function of `createPipelineExecutor` which creates `PipelineExecutor`
7003
+ * @param options - Options for execution, including input parameters, pipeline, and callbacks.
7004
+ * @returns The result of the pipeline execution, including output parameters, errors, and usage statistics.
6661
7005
  *
6662
7006
  * @private internal utility of `createPipelineExecutor`
6663
7007
  */
@@ -6980,6 +7324,22 @@ function createPipelineExecutor(options) {
6980
7324
  cacheDirname,
6981
7325
  intermediateFilesStrategy,
6982
7326
  isAutoInstalled,
7327
+ }).catch((error) => {
7328
+ assertsError(error);
7329
+ return exportJson({
7330
+ name: 'pipelineExecutorResult',
7331
+ message: `Unuccessful PipelineExecutorResult, last catch`,
7332
+ order: [],
7333
+ value: {
7334
+ isSuccessful: false,
7335
+ errors: [serializeError(error)],
7336
+ warnings: [],
7337
+ usage: UNCERTAIN_USAGE,
7338
+ executionReport: null,
7339
+ outputParameters: {},
7340
+ preparedPipeline,
7341
+ },
7342
+ });
6983
7343
  });
6984
7344
  };
6985
7345
  const pipelineExecutor = (inputParameters) => createTask({
@@ -7040,27 +7400,48 @@ async function preparePersona(personaDescription, tools, options) {
7040
7400
  pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-persona.book'),
7041
7401
  tools,
7042
7402
  });
7043
- // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
7044
7403
  const _llms = arrayableToArray(tools.llm);
7045
7404
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
7046
- const availableModels = await llmTools.listModels();
7047
- const availableModelNames = availableModels
7405
+ const availableModels = (await llmTools.listModels())
7048
7406
  .filter(({ modelVariant }) => modelVariant === 'CHAT')
7049
- .map(({ modelName }) => modelName)
7050
- .join(',');
7051
- const result = await preparePersonaExecutor({ availableModelNames, personaDescription }).asPromise();
7407
+ .map(({ modelName, modelDescription }) => ({
7408
+ modelName,
7409
+ modelDescription,
7410
+ // <- Note: `modelTitle` and `modelVariant` is not relevant for this task
7411
+ }));
7412
+ const result = await preparePersonaExecutor({
7413
+ availableModels /* <- Note: Passing as JSON */,
7414
+ personaDescription,
7415
+ }).asPromise();
7052
7416
  const { outputParameters } = result;
7053
- const { modelRequirements: modelRequirementsRaw } = outputParameters;
7054
- const modelRequirements = JSON.parse(modelRequirementsRaw);
7417
+ const { modelsRequirements: modelsRequirementsJson } = outputParameters;
7418
+ let modelsRequirementsUnchecked = jsonParse(modelsRequirementsJson);
7055
7419
  if (isVerbose) {
7056
- console.info(`PERSONA ${personaDescription}`, modelRequirements);
7420
+ console.info(`PERSONA ${personaDescription}`, modelsRequirementsUnchecked);
7057
7421
  }
7058
- const { modelName, systemMessage, temperature } = modelRequirements;
7059
- return {
7422
+ if (!Array.isArray(modelsRequirementsUnchecked)) {
7423
+ // <- TODO: Book should have syntax and system to enforce shape of JSON
7424
+ modelsRequirementsUnchecked = [modelsRequirementsUnchecked];
7425
+ /*
7426
+ throw new UnexpectedError(
7427
+ spaceTrim(
7428
+ (block) => `
7429
+ Invalid \`modelsRequirements\`:
7430
+
7431
+ \`\`\`json
7432
+ ${block(JSON.stringify(modelsRequirementsUnchecked, null, 4))}
7433
+ \`\`\`
7434
+ `,
7435
+ ),
7436
+ );
7437
+ */
7438
+ }
7439
+ const modelsRequirements = modelsRequirementsUnchecked.map((modelRequirements) => ({
7060
7440
  modelVariant: 'CHAT',
7061
- modelName,
7062
- systemMessage,
7063
- temperature,
7441
+ ...modelRequirements,
7442
+ }));
7443
+ return {
7444
+ modelsRequirements,
7064
7445
  };
7065
7446
  }
7066
7447
  /**
@@ -7122,7 +7503,9 @@ function mimeTypeToExtension(value) {
7122
7503
  }
7123
7504
 
7124
7505
  /**
7125
- * @@@
7506
+ * Factory function that creates a handler for processing knowledge sources.
7507
+ * Provides standardized processing of different types of knowledge sources
7508
+ * across various scraper implementations.
7126
7509
  *
7127
7510
  * @public exported from `@promptbook/core`
7128
7511
  */
@@ -7229,7 +7612,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
7229
7612
  > },
7230
7613
  */
7231
7614
  async asJson() {
7232
- return JSON.parse(await tools.fs.readFile(filename, 'utf-8'));
7615
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
7233
7616
  },
7234
7617
  async asText() {
7235
7618
  return await tools.fs.readFile(filename, 'utf-8');
@@ -7363,9 +7746,12 @@ TODO: [🧊] This is how it can look in future
7363
7746
  */
7364
7747
 
7365
7748
  /**
7366
- * @@@
7749
+ * Prepares tasks by adding knowledge to the prompt and ensuring all necessary parameters are included.
7367
7750
  *
7368
- * @public exported from `@promptbook/core`
7751
+ * @param tasks Sequence of tasks that are chained together to form a pipeline
7752
+ * @returns A promise that resolves to the prepared tasks.
7753
+ *
7754
+ * @private internal utility of `preparePipeline`
7369
7755
  */
7370
7756
  async function prepareTasks(pipeline, tools, options) {
7371
7757
  const { maxParallelCount = DEFAULT_MAX_PARALLEL_COUNT } = options;
@@ -7487,14 +7873,14 @@ async function preparePipeline(pipeline, tools, options) {
7487
7873
  // TODO: [🖌][🧠] Implement some `mapAsync` function
7488
7874
  const preparedPersonas = new Array(personas.length);
7489
7875
  await forEachAsync(personas, { maxParallelCount /* <- TODO: [🪂] When there are subtasks, this maximul limit can be broken */ }, async (persona, index) => {
7490
- const modelRequirements = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
7876
+ const { modelsRequirements } = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
7491
7877
  rootDirname,
7492
7878
  maxParallelCount /* <- TODO: [🪂] */,
7493
7879
  isVerbose,
7494
7880
  });
7495
7881
  const preparedPersona = {
7496
7882
  ...persona,
7497
- modelRequirements,
7883
+ modelsRequirements,
7498
7884
  preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id],
7499
7885
  // <- TODO: [🍙] Make some standard order of json properties
7500
7886
  };
@@ -7883,7 +8269,7 @@ const sectionCommandParser = {
7883
8269
  /**
7884
8270
  * Parses the boilerplate command
7885
8271
  *
7886
- * Note: @@@ This command is used as boilerplate for new commands - it should NOT be used in any `.book` file
8272
+ * Note: @@ This command is used as boilerplate for new commands - it should NOT be used in any `.book` file
7887
8273
  *
7888
8274
  * @see `documentationUrl` for more details
7889
8275
  * @private within the commands folder
@@ -8271,11 +8657,11 @@ const expectCommandParser = {
8271
8657
  };
8272
8658
 
8273
8659
  /**
8274
- * @@@
8660
+ * Normalizes a given text to camelCase format.
8275
8661
  *
8276
- * @param text @@@
8277
- * @param _isFirstLetterCapital @@@
8278
- * @returns @@@
8662
+ * @param text The text to be normalized.
8663
+ * @param _isFirstLetterCapital Whether the first letter should be capitalized.
8664
+ * @returns The camelCase formatted string.
8279
8665
  * @example 'helloWorld'
8280
8666
  * @example 'iLovePromptbook'
8281
8667
  * @public exported from `@promptbook/utils`
@@ -8346,11 +8732,12 @@ function removeQuotes(text) {
8346
8732
  }
8347
8733
 
8348
8734
  /**
8349
- * Function `validateParameterName` will @@@
8735
+ * Function `validateParameterName` will normalize and validate a parameter name for use in pipelines.
8736
+ * It removes diacritics, emojis, and quotes, normalizes to camelCase, and checks for reserved names and invalid characters.
8350
8737
  *
8351
- * @param parameterName @@@
8352
- * @returns @@@
8353
- * @throws {ParseError} @@@
8738
+ * @param parameterName The parameter name to validate and normalize.
8739
+ * @returns The validated and normalized parameter name.
8740
+ * @throws {ParseError} If the parameter name is empty, reserved, or contains invalid characters.
8354
8741
  * @private within the repository
8355
8742
  */
8356
8743
  function validateParameterName(parameterName) {
@@ -8420,8 +8807,6 @@ function validateParameterName(parameterName) {
8420
8807
  /**
8421
8808
  * Parses the foreach command
8422
8809
  *
8423
- * Note: @@@ This command is used as foreach for new commands - it should NOT be used in any `.book` file
8424
- *
8425
8810
  * @see `documentationUrl` for more details
8426
8811
  * @public exported from `@promptbook/editable`
8427
8812
  */
@@ -8478,14 +8863,14 @@ const foreachCommandParser = {
8478
8863
  `));
8479
8864
  // <- TODO: [🏢] List all supported format names
8480
8865
  }
8481
- const subvalueDefinition = formatDefinition.subvalueDefinitions.find((subvalueDefinition) => [subvalueDefinition.subvalueName, ...(subvalueDefinition.aliases || [])].includes(subformatName));
8482
- if (subvalueDefinition === undefined) {
8866
+ const subvalueParser = formatDefinition.subvalueParsers.find((subvalueParser) => [subvalueParser.subvalueName, ...(subvalueParser.aliases || [])].includes(subformatName));
8867
+ if (subvalueParser === undefined) {
8483
8868
  throw new ParseError(spaceTrim((block) => `
8484
8869
  Unsupported subformat name "${subformatName}" for format "${formatName}"
8485
8870
 
8486
8871
  Available subformat names for format "${formatDefinition.formatName}":
8487
- ${block(formatDefinition.subvalueDefinitions
8488
- .map((subvalueDefinition) => subvalueDefinition.subvalueName)
8872
+ ${block(formatDefinition.subvalueParsers
8873
+ .map((subvalueParser) => subvalueParser.subvalueName)
8489
8874
  .map((subvalueName) => `- ${subvalueName}`)
8490
8875
  .join('\n'))}
8491
8876
  `));
@@ -8662,14 +9047,14 @@ const formatCommandParser = {
8662
9047
  };
8663
9048
 
8664
9049
  /**
8665
- * @@@
9050
+ * Chatbot form factor definition for conversational interfaces that interact with users in a chat-like manner.
8666
9051
  *
8667
9052
  * @public exported from `@promptbook/core`
8668
9053
  */
8669
9054
  const ChatbotFormfactorDefinition = {
8670
9055
  name: 'CHATBOT',
8671
9056
  aliasNames: ['CHAT'],
8672
- description: `@@@`,
9057
+ description: `A chatbot form factor for conversational user interfaces.`,
8673
9058
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/174`,
8674
9059
  pipelineInterface: {
8675
9060
  inputParameters: [
@@ -8696,7 +9081,45 @@ const ChatbotFormfactorDefinition = {
8696
9081
  };
8697
9082
 
8698
9083
  /**
8699
- * Generator is form of app that @@@
9084
+ * Completion is formfactor that emulates completion models
9085
+ *
9086
+ * @public exported from `@promptbook/core`
9087
+ */
9088
+ const CompletionFormfactorDefinition = {
9089
+ name: 'COMPLETION',
9090
+ description: `Completion is formfactor that emulates completion models`,
9091
+ documentationUrl: `https://github.com/webgptorg/promptbook/discussions/@@`,
9092
+ // <- TODO: https://github.com/webgptorg/promptbook/discussions/new?category=concepts
9093
+ // "🔠 Completion Formfactor"
9094
+ pipelineInterface: {
9095
+ inputParameters: [
9096
+ {
9097
+ name: 'inputText',
9098
+ description: `Input text to be completed`,
9099
+ isInput: true,
9100
+ isOutput: false,
9101
+ },
9102
+ {
9103
+ name: 'instructions',
9104
+ description: `Additional instructions for the model, for example the required length, empty by default`,
9105
+ isInput: true,
9106
+ isOutput: false,
9107
+ },
9108
+ ],
9109
+ outputParameters: [
9110
+ {
9111
+ name: 'followingText',
9112
+ description: `Text that follows the input text`,
9113
+ isInput: false,
9114
+ isOutput: true,
9115
+ },
9116
+ ],
9117
+ },
9118
+ };
9119
+
9120
+ /**
9121
+ * Generator form factor represents an application that generates content or data based on user input or predefined rules.
9122
+ * This form factor is used for apps that produce outputs, such as text, images, or other media, based on provided input.
8700
9123
  *
8701
9124
  * @public exported from `@promptbook/core`
8702
9125
  */
@@ -8725,7 +9148,7 @@ const GeneratorFormfactorDefinition = {
8725
9148
  };
8726
9149
 
8727
9150
  /**
8728
- * @@@
9151
+ * Pipeline interface which is equivalent to `any`
8729
9152
  *
8730
9153
  * @see https://github.com/webgptorg/promptbook/discussions/171
8731
9154
  *
@@ -8740,13 +9163,13 @@ const GENERIC_PIPELINE_INTERFACE = {
8740
9163
  */
8741
9164
 
8742
9165
  /**
8743
- * @@@
9166
+ * A generic pipeline
8744
9167
  *
8745
9168
  * @public exported from `@promptbook/core`
8746
9169
  */
8747
9170
  const GenericFormfactorDefinition = {
8748
9171
  name: 'GENERIC',
8749
- description: `@@@`,
9172
+ description: `A generic pipeline`,
8750
9173
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/173`,
8751
9174
  pipelineInterface: GENERIC_PIPELINE_INTERFACE,
8752
9175
  };
@@ -8781,17 +9204,20 @@ const ImageGeneratorFormfactorDefinition = {
8781
9204
  };
8782
9205
 
8783
9206
  /**
8784
- * Matcher is form of app that @@@
9207
+ * Matcher is form of app that evaluates (spreadsheet) content against defined criteria or patterns,
9208
+ * determining if it matches or meets specific requirements. Used for classification,
9209
+ * validation, filtering, and quality assessment of inputs.
8785
9210
  *
8786
9211
  * @public exported from `@promptbook/core`
8787
9212
  */
8788
9213
  const MatcherFormfactorDefinition = {
8789
9214
  name: 'EXPERIMENTAL_MATCHER',
8790
- description: `@@@`,
9215
+ description: `An evaluation system that determines whether content meets specific criteria or patterns.
9216
+ Used for content validation, quality assessment, and intelligent filtering tasks. Currently in experimental phase.`,
8791
9217
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/177`,
8792
9218
  pipelineInterface: {
8793
9219
  inputParameters: [
8794
- /* @@@ */
9220
+ /* Input parameters for content to be matched and criteria to match against */
8795
9221
  {
8796
9222
  name: 'nonce',
8797
9223
  description: 'Just to prevent EXPERIMENTAL_MATCHER to be set as implicit formfactor',
@@ -8800,20 +9226,21 @@ const MatcherFormfactorDefinition = {
8800
9226
  },
8801
9227
  ],
8802
9228
  outputParameters: [
8803
- /* @@@ */
9229
+ /* Output parameters containing match results, confidence scores, and relevant metadata */
8804
9230
  ],
8805
9231
  },
8806
9232
  };
8807
9233
 
8808
9234
  /**
8809
- * Sheets is form of app that @@@
9235
+ * Sheets is form of app that processes tabular data in CSV format, allowing transformation
9236
+ * and analysis of structured data through AI-powered operations
8810
9237
  *
8811
9238
  * @public exported from `@promptbook/core`
8812
9239
  */
8813
9240
  const SheetsFormfactorDefinition = {
8814
9241
  name: 'SHEETS',
8815
9242
  aliasNames: ['SHEETS', 'SHEET'],
8816
- description: `@@@`,
9243
+ description: `A formfactor for processing spreadsheet-like data in CSV format, enabling AI transformations on tabular data`,
8817
9244
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/176`,
8818
9245
  pipelineInterface: {
8819
9246
  inputParameters: [
@@ -8836,13 +9263,16 @@ const SheetsFormfactorDefinition = {
8836
9263
  };
8837
9264
 
8838
9265
  /**
8839
- * Translator is form of app that @@@
9266
+ * Translator is form of app that transforms input text from one form to another,
9267
+ * such as language translation, style conversion, tone modification, or other text transformations.
8840
9268
  *
8841
9269
  * @public exported from `@promptbook/core`
8842
9270
  */
8843
9271
  const TranslatorFormfactorDefinition = {
8844
9272
  name: 'TRANSLATOR',
8845
- description: `@@@`,
9273
+ description: `A text transformation system that converts input content into different forms,
9274
+ including language translations, paraphrasing, style conversions, and tone adjustments.
9275
+ This form factor takes one input and produces one transformed output.`,
8846
9276
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/175`,
8847
9277
  pipelineInterface: {
8848
9278
  inputParameters: [
@@ -8879,6 +9309,8 @@ const FORMFACTOR_DEFINITIONS = [
8879
9309
  MatcherFormfactorDefinition,
8880
9310
  GeneratorFormfactorDefinition,
8881
9311
  ImageGeneratorFormfactorDefinition,
9312
+ CompletionFormfactorDefinition,
9313
+ // <- [🛬] When making new formfactor, copy the _boilerplate and link it here
8882
9314
  ];
8883
9315
  /**
8884
9316
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -8887,7 +9319,7 @@ const FORMFACTOR_DEFINITIONS = [
8887
9319
  /**
8888
9320
  * Parses the formfactor command
8889
9321
  *
8890
- * Note: @@@ This command is used as formfactor for new commands - it should NOT be used in any `.book` file
9322
+ * Note: This command is used as a formfactor for new commands and defines the app type format - it should NOT be used in any `.book` file
8891
9323
  *
8892
9324
  * @see `documentationUrl` for more details
8893
9325
  * @public exported from `@promptbook/editable`
@@ -8909,7 +9341,7 @@ const formfactorCommandParser = {
8909
9341
  /**
8910
9342
  * Description of the FORMFACTOR command
8911
9343
  */
8912
- description: `@@`,
9344
+ description: `Specifies the application type and interface requirements that this promptbook should conform to`,
8913
9345
  /**
8914
9346
  * Link to documentation
8915
9347
  */
@@ -9052,8 +9484,7 @@ const jokerCommandParser = {
9052
9484
  };
9053
9485
 
9054
9486
  /**
9055
- * @@@
9056
- *
9487
+ * @see {@link ModelVariant}
9057
9488
  * @public exported from `@promptbook/core`
9058
9489
  */
9059
9490
  const MODEL_VARIANTS = ['COMPLETION', 'CHAT', 'EMBEDDING' /* <- TODO [🏳] */ /* <- [🤖] */];
@@ -9485,10 +9916,10 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
9485
9916
  }
9486
9917
 
9487
9918
  /**
9488
- * @@@
9919
+ * Checks if the given value is a valid JavaScript identifier name.
9489
9920
  *
9490
- * @param javascriptName @@@
9491
- * @returns @@@
9921
+ * @param javascriptName The value to check for JavaScript identifier validity.
9922
+ * @returns `true` if the value is a valid JavaScript name, false otherwise.
9492
9923
  * @public exported from `@promptbook/utils`
9493
9924
  */
9494
9925
  function isValidJavascriptName(javascriptName) {
@@ -9968,7 +10399,10 @@ function parseCommand(raw, usagePlace) {
9968
10399
  `));
9969
10400
  }
9970
10401
  /**
9971
- * @@@
10402
+ * Generates a markdown-formatted message listing all supported commands
10403
+ * with their descriptions and documentation links
10404
+ *
10405
+ * @returns A formatted markdown string containing all available commands and their details
9972
10406
  */
9973
10407
  function getSupportedCommandsMessage() {
9974
10408
  return COMMANDS.flatMap(({ name, aliasNames, description, documentationUrl }) =>
@@ -9979,7 +10413,10 @@ function getSupportedCommandsMessage() {
9979
10413
  ]).join('\n');
9980
10414
  }
9981
10415
  /**
9982
- * @@@
10416
+ * Attempts to parse a command variant using the provided input parameters
10417
+ *
10418
+ * @param input Object containing command parsing information including raw command text and normalized values
10419
+ * @returns A parsed Command object if successful, or null if the command cannot be parsed
9983
10420
  */
9984
10421
  function parseCommandVariant(input) {
9985
10422
  const { commandNameRaw, usagePlace, normalized, args, raw, rawArgs } = input;
@@ -10026,7 +10463,7 @@ function parseCommandVariant(input) {
10026
10463
  }
10027
10464
 
10028
10465
  /**
10029
- * @@@
10466
+ * Extracts the interface (input and output parameters) from a pipeline.
10030
10467
  *
10031
10468
  * @deprecated https://github.com/webgptorg/promptbook/pull/186
10032
10469
  * @see https://github.com/webgptorg/promptbook/discussions/171
@@ -10059,7 +10496,7 @@ function getPipelineInterface(pipeline) {
10059
10496
  }
10060
10497
 
10061
10498
  /**
10062
- * @@@
10499
+ * Checks if two pipeline interfaces are structurally identical.
10063
10500
  *
10064
10501
  * @deprecated https://github.com/webgptorg/promptbook/pull/186
10065
10502
  * @see https://github.com/webgptorg/promptbook/discussions/171
@@ -10091,10 +10528,11 @@ function isPipelineInterfacesEqual(pipelineInterface1, pipelineInterface2) {
10091
10528
  }
10092
10529
 
10093
10530
  /**
10094
- * @@@
10531
+ * Checks if a given pipeline satisfies the requirements of a specified pipeline interface.
10095
10532
  *
10096
10533
  * @deprecated https://github.com/webgptorg/promptbook/pull/186
10097
10534
  * @see https://github.com/webgptorg/promptbook/discussions/171
10535
+ * @returns `true` if the pipeline implements the interface, `false` otherwise.
10098
10536
  *
10099
10537
  * @public exported from `@promptbook/core`
10100
10538
  */
@@ -10280,7 +10718,8 @@ function removeMarkdownComments(content) {
10280
10718
  }
10281
10719
 
10282
10720
  /**
10283
- * @@@
10721
+ * Utility to determine if a pipeline string is in flat format.
10722
+ * A flat pipeline is a simple text without proper structure (headers, blocks, etc).
10284
10723
  *
10285
10724
  * @public exported from `@promptbook/editable`
10286
10725
  */
@@ -10301,7 +10740,10 @@ function isFlatPipeline(pipelineString) {
10301
10740
  }
10302
10741
 
10303
10742
  /**
10304
- * @@@
10743
+ * Converts a pipeline structure to its string representation.
10744
+ *
10745
+ * Transforms a flat, simple pipeline into a properly formatted pipeline string
10746
+ * with sections for title, prompt, and return statement.
10305
10747
  *
10306
10748
  * @public exported from `@promptbook/editable`
10307
10749
  */
@@ -10358,7 +10800,7 @@ function deflatePipeline(pipelineString) {
10358
10800
  * Note: It can not work with html syntax and comments
10359
10801
  *
10360
10802
  * @param markdown any valid markdown
10361
- * @returns @@@
10803
+ * @returns An array of strings, each representing an individual list item found in the markdown
10362
10804
  * @public exported from `@promptbook/markdown-utils`
10363
10805
  */
10364
10806
  function extractAllListItemsFromMarkdown(markdown) {
@@ -11021,45 +11463,43 @@ async function compilePipeline(pipelineString, tools, options) {
11021
11463
  */
11022
11464
  function renderPromptbookMermaid(pipelineJson, options) {
11023
11465
  const { linkTask = () => null } = options || {};
11466
+ const MERMAID_PREFIX = 'pipeline_';
11467
+ const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
11468
+ const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
11469
+ const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
11470
+ const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
11024
11471
  const parameterNameToTaskName = (parameterName) => {
11472
+ if (parameterName === 'knowledge') {
11473
+ return MERMAID_KNOWLEDGE_NAME;
11474
+ }
11475
+ else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
11476
+ return MERMAID_RESERVED_NAME;
11477
+ }
11025
11478
  const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
11026
11479
  if (!parameter) {
11027
11480
  throw new UnexpectedError(`Could not find {${parameterName}}`);
11028
- // <- TODO: !!6 This causes problems when {knowledge} and other reserved parameters are used
11481
+ // <- TODO: This causes problems when {knowledge} and other reserved parameters are used
11029
11482
  }
11030
11483
  if (parameter.isInput) {
11031
- return 'input';
11484
+ return MERMAID_INPUT_NAME;
11032
11485
  }
11033
11486
  const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
11034
11487
  if (!task) {
11035
11488
  throw new Error(`Could not find task for {${parameterName}}`);
11036
11489
  }
11037
- return task.name || normalizeTo_camelCase('task-' + titleToName(task.title));
11490
+ return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
11038
11491
  };
11039
- const promptbookMermaid = spaceTrim$1((block) => `
11040
-
11041
- %% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
11042
-
11043
- flowchart LR
11044
- subgraph "${pipelineJson.title}"
11045
-
11046
- direction TB
11047
-
11048
- input((Input)):::input
11049
- ${block(pipelineJson.tasks
11492
+ const inputAndIntermediateParametersMermaid = pipelineJson.tasks
11050
11493
  .flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
11051
11494
  `${parameterNameToTaskName(resultingParameterName)}("${title}")`,
11052
11495
  ...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
11053
11496
  ])
11054
- .join('\n'))}
11055
-
11056
- ${block(pipelineJson.parameters
11497
+ .join('\n');
11498
+ const outputParametersMermaid = pipelineJson.parameters
11057
11499
  .filter(({ isOutput }) => isOutput)
11058
- .map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->output`)
11059
- .join('\n'))}
11060
- output((Output)):::output
11061
-
11062
- ${block(pipelineJson.tasks
11500
+ .map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
11501
+ .join('\n');
11502
+ const linksMermaid = pipelineJson.tasks
11063
11503
  .map((task) => {
11064
11504
  const link = linkTask(task);
11065
11505
  if (link === null) {
@@ -11070,10 +11510,44 @@ function renderPromptbookMermaid(pipelineJson, options) {
11070
11510
  return `click ${taskName} href "${href}" "${title}";`;
11071
11511
  })
11072
11512
  .filter((line) => line !== '')
11073
- .join('\n'))}
11513
+ .join('\n');
11514
+ const interactionPointsMermaid = Object.entries({
11515
+ [MERMAID_INPUT_NAME]: 'Input',
11516
+ [MERMAID_OUTPUT_NAME]: 'Output',
11517
+ [MERMAID_RESERVED_NAME]: 'Other',
11518
+ [MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
11519
+ })
11520
+ .filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
11521
+ .map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
11522
+ .join('\n');
11523
+ const promptbookMermaid = spaceTrim$1((block) => `
11524
+
11525
+ %% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
11526
+
11527
+ flowchart LR
11528
+ subgraph "${pipelineJson.title}"
11529
+
11530
+ %% Basic configuration
11531
+ direction TB
11074
11532
 
11075
- classDef input color: grey;
11076
- classDef output color: grey;
11533
+ %% Interaction points from pipeline to outside
11534
+ ${block(interactionPointsMermaid)}
11535
+
11536
+ %% Input and intermediate parameters
11537
+ ${block(inputAndIntermediateParametersMermaid)}
11538
+
11539
+
11540
+ %% Output parameters
11541
+ ${block(outputParametersMermaid)}
11542
+
11543
+ %% Links
11544
+ ${block(linksMermaid)}
11545
+
11546
+ %% Styles
11547
+ classDef ${MERMAID_INPUT_NAME} color: grey;
11548
+ classDef ${MERMAID_OUTPUT_NAME} color: grey;
11549
+ classDef ${MERMAID_RESERVED_NAME} color: grey;
11550
+ classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
11077
11551
 
11078
11552
  end;
11079
11553
 
@@ -11113,11 +11587,11 @@ function parseKeywordsFromString(input) {
11113
11587
  }
11114
11588
 
11115
11589
  /**
11116
- * @@@
11590
+ * Converts a name string into a URI-compatible format.
11117
11591
  *
11118
- * @param name @@@
11119
- * @returns @@@
11120
- * @example @@@
11592
+ * @param name The string to be converted to a URI-compatible format.
11593
+ * @returns A URI-compatible string derived from the input name.
11594
+ * @example 'Hello World' -> 'hello-world'
11121
11595
  * @public exported from `@promptbook/utils`
11122
11596
  */
11123
11597
  function nameToUriPart(name) {
@@ -11131,11 +11605,11 @@ function nameToUriPart(name) {
11131
11605
  }
11132
11606
 
11133
11607
  /**
11134
- * @@@
11608
+ * Converts a given name into URI-compatible parts.
11135
11609
  *
11136
- * @param name @@@
11137
- * @returns @@@
11138
- * @example @@@
11610
+ * @param name The name to be converted into URI parts.
11611
+ * @returns An array of URI-compatible parts derived from the name.
11612
+ * @example 'Example Name' -> ['example', 'name']
11139
11613
  * @public exported from `@promptbook/utils`
11140
11614
  */
11141
11615
  function nameToUriParts(name) {
@@ -12934,7 +13408,7 @@ function $initializeRunCommand(program) {
12934
13408
  }
12935
13409
  let inputParameters = {};
12936
13410
  if (json) {
12937
- inputParameters = JSON.parse(json);
13411
+ inputParameters = jsonParse(json);
12938
13412
  // <- TODO: Maybe check shape of passed JSON and if its valid parameters Record
12939
13413
  }
12940
13414
  // TODO: DRY [◽]
@@ -12986,7 +13460,9 @@ function $initializeRunCommand(program) {
12986
13460
  type: 'text',
12987
13461
  name: 'pipelineSource',
12988
13462
  message: '',
12989
- validate: (value) => (value.length > 0 ? true : 'Pipeline source is required'),
13463
+ validate(value) {
13464
+ return value.length > 0 ? true : 'Pipeline source is required';
13465
+ },
12990
13466
  });
12991
13467
  if (!response.pipelineSource) {
12992
13468
  console.error(colors.red('Pipeline source is required'));
@@ -13111,10 +13587,10 @@ function $initializeRunCommand(program) {
13111
13587
  console.info(colors.gray('--- Detailed Result ---'));
13112
13588
  console.info({ isSuccessful, errors, warnings, outputParameters, executionReport });
13113
13589
  }
13114
- if (saveReport && saveReport.endsWith('.json')) {
13590
+ if (executionReport !== null && saveReport && saveReport.endsWith('.json')) {
13115
13591
  await writeFile(saveReport, JSON.stringify(executionReport, null, 4) + '\n', 'utf-8');
13116
13592
  }
13117
- else if (saveReport && saveReport.endsWith('.md')) {
13593
+ else if (executionReport !== null && saveReport && saveReport.endsWith('.md')) {
13118
13594
  const executionReportString = executionReportJsonToString(executionReport);
13119
13595
  await writeFile(saveReport, executionReportString, 'utf-8');
13120
13596
  }
@@ -13157,15 +13633,15 @@ function $initializeRunCommand(program) {
13157
13633
  * TODO: [🖇] What about symlinks? Maybe flag --follow-symlinks
13158
13634
  */
13159
13635
 
13160
- // TODO: !!!! List running services from REMOTE_SERVER_URLS
13161
- // TODO: !!!! Import directly from YML
13636
+ // TODO: [🥺] List running services from REMOTE_SERVER_URLS
13637
+ // TODO: [🥺] Import directly from YML
13162
13638
  /**
13163
- * @private !!!! Decide how to expose this
13639
+ * @private [🥺] Decide how to expose this
13164
13640
  */
13165
13641
  const openapiJson = {
13166
13642
  openapi: '3.0.0',
13167
13643
  info: {
13168
- title: 'Promptbook Remote Server API (!!!! From TS)',
13644
+ title: 'Promptbook Remote Server API ([🥺] From YML)',
13169
13645
  version: '1.0.0',
13170
13646
  description: 'API documentation for the Promptbook Remote Server',
13171
13647
  },
@@ -13177,6 +13653,13 @@ const openapiJson = {
13177
13653
  responses: {
13178
13654
  '200': {
13179
13655
  description: 'Server details in markdown format.',
13656
+ content: {
13657
+ 'text/markdown': {
13658
+ schema: {
13659
+ type: 'string',
13660
+ },
13661
+ },
13662
+ },
13180
13663
  },
13181
13664
  },
13182
13665
  },
@@ -13207,13 +13690,22 @@ const openapiJson = {
13207
13690
  },
13208
13691
  },
13209
13692
  responses: {
13210
- '200': {
13693
+ '201': {
13211
13694
  description: 'Successful login',
13212
13695
  content: {
13213
13696
  'application/json': {
13214
13697
  schema: {
13215
13698
  type: 'object',
13216
13699
  properties: {
13700
+ isSuccess: {
13701
+ type: 'boolean',
13702
+ },
13703
+ message: {
13704
+ type: 'string',
13705
+ },
13706
+ error: {
13707
+ type: 'object',
13708
+ },
13217
13709
  identification: {
13218
13710
  type: 'object',
13219
13711
  },
@@ -13222,6 +13714,43 @@ const openapiJson = {
13222
13714
  },
13223
13715
  },
13224
13716
  },
13717
+ '400': {
13718
+ description: 'Bad request or login failed',
13719
+ content: {
13720
+ 'application/json': {
13721
+ schema: {
13722
+ type: 'object',
13723
+ properties: {
13724
+ error: {
13725
+ type: 'object',
13726
+ },
13727
+ },
13728
+ },
13729
+ },
13730
+ },
13731
+ },
13732
+ '401': {
13733
+ description: 'Authentication error',
13734
+ content: {
13735
+ 'application/json': {
13736
+ schema: {
13737
+ type: 'object',
13738
+ properties: {
13739
+ isSuccess: {
13740
+ type: 'boolean',
13741
+ enum: [false],
13742
+ },
13743
+ message: {
13744
+ type: 'string',
13745
+ },
13746
+ error: {
13747
+ type: 'object',
13748
+ },
13749
+ },
13750
+ },
13751
+ },
13752
+ },
13753
+ },
13225
13754
  },
13226
13755
  },
13227
13756
  },
@@ -13243,6 +13772,16 @@ const openapiJson = {
13243
13772
  },
13244
13773
  },
13245
13774
  },
13775
+ '500': {
13776
+ description: 'No collection available',
13777
+ content: {
13778
+ 'text/plain': {
13779
+ schema: {
13780
+ type: 'string',
13781
+ },
13782
+ },
13783
+ },
13784
+ },
13246
13785
  },
13247
13786
  },
13248
13787
  },
@@ -13274,6 +13813,28 @@ const openapiJson = {
13274
13813
  },
13275
13814
  '404': {
13276
13815
  description: 'Book not found.',
13816
+ content: {
13817
+ 'application/json': {
13818
+ schema: {
13819
+ type: 'object',
13820
+ properties: {
13821
+ error: {
13822
+ type: 'object',
13823
+ },
13824
+ },
13825
+ },
13826
+ },
13827
+ },
13828
+ },
13829
+ '500': {
13830
+ description: 'No collection available',
13831
+ content: {
13832
+ 'text/plain': {
13833
+ schema: {
13834
+ type: 'string',
13835
+ },
13836
+ },
13837
+ },
13277
13838
  },
13278
13839
  },
13279
13840
  },
@@ -13291,6 +13852,28 @@ const openapiJson = {
13291
13852
  type: 'array',
13292
13853
  items: {
13293
13854
  type: 'object',
13855
+ properties: {
13856
+ nonce: {
13857
+ type: 'string',
13858
+ },
13859
+ taskId: {
13860
+ type: 'string',
13861
+ },
13862
+ taskType: {
13863
+ type: 'string',
13864
+ },
13865
+ status: {
13866
+ type: 'string',
13867
+ },
13868
+ createdAt: {
13869
+ type: 'string',
13870
+ format: 'date-time',
13871
+ },
13872
+ updatedAt: {
13873
+ type: 'string',
13874
+ format: 'date-time',
13875
+ },
13876
+ },
13294
13877
  },
13295
13878
  },
13296
13879
  },
@@ -13299,51 +13882,350 @@ const openapiJson = {
13299
13882
  },
13300
13883
  },
13301
13884
  },
13302
- '/executions/new': {
13303
- post: {
13304
- summary: 'Start a new execution',
13305
- description: 'Starts a new execution task for a given pipeline.',
13306
- requestBody: {
13307
- required: true,
13308
- content: {
13309
- 'application/json': {
13310
- schema: {
13311
- type: 'object',
13312
- properties: {
13313
- pipelineUrl: {
13314
- type: 'string',
13315
- },
13316
- inputParameters: {
13317
- type: 'object',
13318
- },
13319
- identification: {
13320
- type: 'object',
13321
- },
13322
- },
13323
- },
13324
- },
13325
- },
13326
- },
13885
+ '/executions/last': {
13886
+ get: {
13887
+ summary: 'Get the last execution',
13888
+ description: 'Returns details of the last execution task.',
13327
13889
  responses: {
13328
13890
  '200': {
13329
- description: 'The newly created execution task.',
13891
+ description: 'The last execution task with full details.',
13330
13892
  content: {
13331
13893
  'application/json': {
13332
13894
  schema: {
13333
13895
  type: 'object',
13334
- },
13335
- },
13336
- },
13337
- },
13338
- '400': {
13339
- description: 'Invalid input.',
13896
+ properties: {
13897
+ nonce: {
13898
+ type: 'string',
13899
+ },
13900
+ taskId: {
13901
+ type: 'string',
13902
+ },
13903
+ taskType: {
13904
+ type: 'string',
13905
+ },
13906
+ status: {
13907
+ type: 'string',
13908
+ },
13909
+ errors: {
13910
+ type: 'array',
13911
+ items: {
13912
+ type: 'object',
13913
+ },
13914
+ },
13915
+ warnings: {
13916
+ type: 'array',
13917
+ items: {
13918
+ type: 'object',
13919
+ },
13920
+ },
13921
+ createdAt: {
13922
+ type: 'string',
13923
+ format: 'date-time',
13924
+ },
13925
+ updatedAt: {
13926
+ type: 'string',
13927
+ format: 'date-time',
13928
+ },
13929
+ currentValue: {
13930
+ type: 'object',
13931
+ },
13932
+ },
13933
+ },
13934
+ },
13935
+ },
13936
+ },
13937
+ '404': {
13938
+ description: 'No execution tasks found.',
13939
+ content: {
13940
+ 'text/plain': {
13941
+ schema: {
13942
+ type: 'string',
13943
+ },
13944
+ },
13945
+ },
13946
+ },
13947
+ },
13948
+ },
13949
+ },
13950
+ '/executions/{taskId}': {
13951
+ get: {
13952
+ summary: 'Get specific execution',
13953
+ description: 'Returns details of a specific execution task.',
13954
+ parameters: [
13955
+ {
13956
+ in: 'path',
13957
+ name: 'taskId',
13958
+ required: true,
13959
+ schema: {
13960
+ type: 'string',
13961
+ },
13962
+ description: 'The ID of the execution task to retrieve.',
13963
+ },
13964
+ ],
13965
+ responses: {
13966
+ '200': {
13967
+ description: 'The execution task with full details.',
13968
+ content: {
13969
+ 'application/json': {
13970
+ schema: {
13971
+ type: 'object',
13972
+ properties: {
13973
+ nonce: {
13974
+ type: 'string',
13975
+ },
13976
+ taskId: {
13977
+ type: 'string',
13978
+ },
13979
+ taskType: {
13980
+ type: 'string',
13981
+ },
13982
+ status: {
13983
+ type: 'string',
13984
+ },
13985
+ errors: {
13986
+ type: 'array',
13987
+ items: {
13988
+ type: 'object',
13989
+ },
13990
+ },
13991
+ warnings: {
13992
+ type: 'array',
13993
+ items: {
13994
+ type: 'object',
13995
+ },
13996
+ },
13997
+ createdAt: {
13998
+ type: 'string',
13999
+ format: 'date-time',
14000
+ },
14001
+ updatedAt: {
14002
+ type: 'string',
14003
+ format: 'date-time',
14004
+ },
14005
+ currentValue: {
14006
+ type: 'object',
14007
+ },
14008
+ },
14009
+ },
14010
+ },
14011
+ },
14012
+ },
14013
+ '404': {
14014
+ description: 'Execution task not found.',
14015
+ content: {
14016
+ 'text/plain': {
14017
+ schema: {
14018
+ type: 'string',
14019
+ },
14020
+ },
14021
+ },
14022
+ },
14023
+ },
14024
+ },
14025
+ },
14026
+ '/executions/new': {
14027
+ post: {
14028
+ summary: 'Start a new execution',
14029
+ description: 'Starts a new execution task for a given pipeline.',
14030
+ requestBody: {
14031
+ required: true,
14032
+ content: {
14033
+ 'application/json': {
14034
+ schema: {
14035
+ type: 'object',
14036
+ properties: {
14037
+ pipelineUrl: {
14038
+ type: 'string',
14039
+ description: 'URL of the pipeline to execute',
14040
+ },
14041
+ book: {
14042
+ type: 'string',
14043
+ description: 'Alternative field for pipelineUrl',
14044
+ },
14045
+ inputParameters: {
14046
+ type: 'object',
14047
+ description: 'Parameters for pipeline execution',
14048
+ },
14049
+ identification: {
14050
+ type: 'object',
14051
+ description: 'User identification data',
14052
+ },
14053
+ },
14054
+ },
14055
+ },
14056
+ },
14057
+ },
14058
+ responses: {
14059
+ '200': {
14060
+ description: 'The newly created execution task.',
14061
+ content: {
14062
+ 'application/json': {
14063
+ schema: {
14064
+ type: 'object',
14065
+ },
14066
+ },
14067
+ },
14068
+ },
14069
+ '400': {
14070
+ description: 'Invalid input.',
14071
+ content: {
14072
+ 'application/json': {
14073
+ schema: {
14074
+ type: 'object',
14075
+ properties: {
14076
+ error: {
14077
+ type: 'object',
14078
+ },
14079
+ },
14080
+ },
14081
+ },
14082
+ },
14083
+ },
14084
+ '404': {
14085
+ description: 'Pipeline not found.',
14086
+ content: {
14087
+ 'text/plain': {
14088
+ schema: {
14089
+ type: 'string',
14090
+ },
14091
+ },
14092
+ },
14093
+ },
14094
+ },
14095
+ },
14096
+ },
14097
+ '/api-docs': {
14098
+ get: {
14099
+ summary: 'API documentation UI',
14100
+ description: 'Swagger UI for API documentation',
14101
+ responses: {
14102
+ '200': {
14103
+ description: 'HTML Swagger UI',
14104
+ },
14105
+ },
14106
+ },
14107
+ },
14108
+ '/swagger': {
14109
+ get: {
14110
+ summary: 'API documentation UI (alternative path)',
14111
+ description: 'Swagger UI for API documentation',
14112
+ responses: {
14113
+ '200': {
14114
+ description: 'HTML Swagger UI',
14115
+ },
14116
+ },
14117
+ },
14118
+ },
14119
+ '/openapi': {
14120
+ get: {
14121
+ summary: 'OpenAPI specification',
14122
+ description: 'Returns the OpenAPI JSON specification',
14123
+ responses: {
14124
+ '200': {
14125
+ description: 'OpenAPI specification',
14126
+ content: {
14127
+ 'application/json': {
14128
+ schema: {
14129
+ type: 'object',
14130
+ },
14131
+ },
14132
+ },
13340
14133
  },
13341
14134
  },
13342
14135
  },
13343
14136
  },
13344
14137
  },
13345
- components: {},
13346
- tags: [],
14138
+ components: {
14139
+ schemas: {
14140
+ Error: {
14141
+ type: 'object',
14142
+ properties: {
14143
+ error: {
14144
+ type: 'object',
14145
+ },
14146
+ },
14147
+ },
14148
+ ExecutionTaskSummary: {
14149
+ type: 'object',
14150
+ properties: {
14151
+ nonce: {
14152
+ type: 'string',
14153
+ },
14154
+ taskId: {
14155
+ type: 'string',
14156
+ },
14157
+ taskType: {
14158
+ type: 'string',
14159
+ },
14160
+ status: {
14161
+ type: 'string',
14162
+ },
14163
+ createdAt: {
14164
+ type: 'string',
14165
+ format: 'date-time',
14166
+ },
14167
+ updatedAt: {
14168
+ type: 'string',
14169
+ format: 'date-time',
14170
+ },
14171
+ },
14172
+ },
14173
+ ExecutionTaskFull: {
14174
+ type: 'object',
14175
+ properties: {
14176
+ nonce: {
14177
+ type: 'string',
14178
+ },
14179
+ taskId: {
14180
+ type: 'string',
14181
+ },
14182
+ taskType: {
14183
+ type: 'string',
14184
+ },
14185
+ status: {
14186
+ type: 'string',
14187
+ },
14188
+ errors: {
14189
+ type: 'array',
14190
+ items: {
14191
+ type: 'object',
14192
+ },
14193
+ },
14194
+ warnings: {
14195
+ type: 'array',
14196
+ items: {
14197
+ type: 'object',
14198
+ },
14199
+ },
14200
+ createdAt: {
14201
+ type: 'string',
14202
+ format: 'date-time',
14203
+ },
14204
+ updatedAt: {
14205
+ type: 'string',
14206
+ format: 'date-time',
14207
+ },
14208
+ currentValue: {
14209
+ type: 'object',
14210
+ },
14211
+ },
14212
+ },
14213
+ },
14214
+ },
14215
+ tags: [
14216
+ {
14217
+ name: 'Books',
14218
+ description: 'Operations related to books and pipelines',
14219
+ },
14220
+ {
14221
+ name: 'Executions',
14222
+ description: 'Operations related to execution tasks',
14223
+ },
14224
+ {
14225
+ name: 'Authentication',
14226
+ description: 'Authentication operations',
14227
+ },
14228
+ ],
13347
14229
  };
13348
14230
  /**
13349
14231
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -13417,7 +14299,7 @@ function startRemoteServer(options) {
13417
14299
  response.setHeader('X-Powered-By', 'Promptbook engine');
13418
14300
  next();
13419
14301
  });
13420
- // TODO: !!!! Expose openapiJson to consumer and also allow to add new routes
14302
+ // TODO: [🥺] Expose openapiJson to consumer and also allow to add new routes
13421
14303
  app.use(OpenApiValidator.middleware({
13422
14304
  apiSpec: openapiJson,
13423
14305
  ignorePaths(path) {
@@ -13714,6 +14596,7 @@ function startRemoteServer(options) {
13714
14596
  promptResult = await llm.callCompletionModel(prompt);
13715
14597
  break;
13716
14598
  case 'EMBEDDING':
14599
+ console.log('!!! llm (EMBEDDING)', llm);
13717
14600
  if (llm.callEmbeddingModel === undefined) {
13718
14601
  // Note: [0] This check should not be a thing
13719
14602
  throw new PipelineExecutionError(`Embedding model is not available`);
@@ -13988,7 +14871,7 @@ function $initializeTestCommand(program) {
13988
14871
  }
13989
14872
  }
13990
14873
  if (filename.endsWith('.bookc')) {
13991
- pipeline = JSON.parse(await readFile(filename, 'utf-8'));
14874
+ pipeline = jsonParse(await readFile(filename, 'utf-8'));
13992
14875
  }
13993
14876
  else {
13994
14877
  if (isVerbose) {
@@ -14111,9 +14994,11 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
14111
14994
  packageName: '@promptbook/anthropic-claude',
14112
14995
  className: 'AnthropicClaudeExecutionTools',
14113
14996
  envVariables: ['ANTHROPIC_CLAUDE_API_KEY'],
14997
+ trustLevel: 'CLOSED',
14998
+ order: MODEL_ORDERS.TOP_TIER,
14114
14999
  getBoilerplateConfiguration() {
14115
15000
  return {
14116
- title: 'Anthropic Claude (boilerplate)',
15001
+ title: 'Anthropic Claude',
14117
15002
  packageName: '@promptbook/anthropic-claude',
14118
15003
  className: 'AnthropicClaudeExecutionTools',
14119
15004
  options: {
@@ -14155,7 +15040,7 @@ function computeUsage(value) {
14155
15040
  /**
14156
15041
  * List of available Anthropic Claude models with pricing
14157
15042
  *
14158
- * Note: Done at 2024-08-16
15043
+ * Note: Done at 2025-05-06
14159
15044
  *
14160
15045
  * @see https://docs.anthropic.com/en/docs/models-overview
14161
15046
  * @public exported from `@promptbook/anthropic-claude`
@@ -14169,8 +15054,8 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
14169
15054
  modelName: 'claude-3-5-sonnet-20240620',
14170
15055
  modelDescription: 'Latest Claude model with great reasoning, coding, and language understanding capabilities. 200K context window. Optimized balance of intelligence and speed.',
14171
15056
  pricing: {
14172
- prompt: computeUsage(`$3.00 / 1M tokens`),
14173
- output: computeUsage(`$15.00 / 1M tokens`),
15057
+ prompt: computeUsage(`$2.50 / 1M tokens`),
15058
+ output: computeUsage(`$12.50 / 1M tokens`),
14174
15059
  },
14175
15060
  },
14176
15061
  {
@@ -14179,8 +15064,8 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
14179
15064
  modelName: 'claude-3-opus-20240229',
14180
15065
  modelDescription: 'Most capable Claude model excelling at complex reasoning, coding, and detailed instruction following. 200K context window. Best for sophisticated tasks requiring nuanced understanding.',
14181
15066
  pricing: {
14182
- prompt: computeUsage(`$15.00 / 1M tokens`),
14183
- output: computeUsage(`$75.00 / 1M tokens`),
15067
+ prompt: computeUsage(`$12.00 / 1M tokens`),
15068
+ output: computeUsage(`$60.00 / 1M tokens`),
14184
15069
  },
14185
15070
  },
14186
15071
  {
@@ -14225,7 +15110,7 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
14225
15110
  },
14226
15111
  {
14227
15112
  modelVariant: 'CHAT',
14228
- modelTitle: ' Claude Instant 1.2',
15113
+ modelTitle: 'Claude Instant 1.2',
14229
15114
  modelName: 'claude-instant-1.2',
14230
15115
  modelDescription: 'Older, faster Claude model optimized for high throughput applications. Lower cost but less capable than newer models. 100K context window.',
14231
15116
  pricing: {
@@ -14239,8 +15124,8 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
14239
15124
  modelName: 'claude-3-7-sonnet-20250219',
14240
15125
  modelDescription: 'Latest generation Claude model with advanced reasoning and language understanding. Enhanced capabilities over 3.5 with improved domain knowledge. 200K context window.',
14241
15126
  pricing: {
14242
- prompt: computeUsage(`$3.00 / 1M tokens`),
14243
- output: computeUsage(`$15.00 / 1M tokens`),
15127
+ prompt: computeUsage(`$2.50 / 1M tokens`),
15128
+ output: computeUsage(`$12.50 / 1M tokens`),
14244
15129
  },
14245
15130
  },
14246
15131
  {
@@ -14287,14 +15172,18 @@ function computeUsageCounts(content) {
14287
15172
  /**
14288
15173
  * Make UncertainNumber
14289
15174
  *
14290
- * @param value
15175
+ * @param value value of the uncertain number, if `NaN` or `undefined`, it will be set to 0 and `isUncertain=true`
15176
+ * @param isUncertain if `true`, the value is uncertain, otherwise depends on the value
14291
15177
  *
14292
15178
  * @private utility for initializating UncertainNumber
14293
15179
  */
14294
- function uncertainNumber(value) {
15180
+ function uncertainNumber(value, isUncertain) {
14295
15181
  if (value === null || value === undefined || Number.isNaN(value)) {
14296
15182
  return UNCERTAIN_ZERO_VALUE;
14297
15183
  }
15184
+ if (isUncertain === true) {
15185
+ return { value, isUncertain };
15186
+ }
14298
15187
  return { value };
14299
15188
  }
14300
15189
 
@@ -14649,9 +15538,11 @@ const _AzureOpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
14649
15538
  packageName: '@promptbook/azure-openai',
14650
15539
  className: 'AzureOpenAiExecutionTools',
14651
15540
  envVariables: ['AZUREOPENAI_RESOURCE_NAME', 'AZUREOPENAI_DEPLOYMENT_NAME', 'AZUREOPENAI_API_KEY'],
15541
+ trustLevel: 'CLOSED_BUSINESS',
15542
+ order: MODEL_ORDERS.NORMAL,
14652
15543
  getBoilerplateConfiguration() {
14653
15544
  return {
14654
- title: 'Azure Open AI (boilerplate)',
15545
+ title: 'Azure Open AI',
14655
15546
  packageName: '@promptbook/azure-openai',
14656
15547
  className: 'AzureOpenAiExecutionTools',
14657
15548
  options: {
@@ -14706,7 +15597,7 @@ const _AzureOpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
14706
15597
  /**
14707
15598
  * List of available OpenAI models with pricing
14708
15599
  *
14709
- * Note: Done at 2024-05-20
15600
+ * Note: Done at 2025-05-06
14710
15601
  *
14711
15602
  * @see https://platform.openai.com/docs/models/
14712
15603
  * @see https://openai.com/api/pricing/
@@ -14735,7 +15626,7 @@ const OPENAI_MODELS = exportJson({
14735
15626
  modelDescription: 'Legacy completion model with strong performance on text generation tasks. Optimized for complex instructions and longer outputs.',
14736
15627
  pricing: {
14737
15628
  prompt: computeUsage(`$2.00 / 1M tokens`),
14738
- output: computeUsage(`$2.00 / 1M tokens`), // <- not sure
15629
+ output: computeUsage(`$2.00 / 1M tokens`),
14739
15630
  },
14740
15631
  },
14741
15632
  /**/
@@ -14877,8 +15768,8 @@ const OPENAI_MODELS = exportJson({
14877
15768
  modelName: 'gpt-3.5-turbo',
14878
15769
  modelDescription: 'Latest version of GPT-3.5 Turbo with improved performance and instruction following capabilities. Default 4K context window with options for 16K.',
14879
15770
  pricing: {
14880
- prompt: computeUsage(`$3.00 / 1M tokens`),
14881
- output: computeUsage(`$6.00 / 1M tokens`), // <- Not sure, refer to gpt-3.5-turbo in Fine-tuning models
15771
+ prompt: computeUsage(`$0.50 / 1M tokens`),
15772
+ output: computeUsage(`$1.50 / 1M tokens`),
14882
15773
  },
14883
15774
  },
14884
15775
  /**/
@@ -14902,7 +15793,7 @@ const OPENAI_MODELS = exportJson({
14902
15793
  modelDescription: 'Efficient legacy completion model with a good balance of performance and speed. Suitable for straightforward text generation tasks.',
14903
15794
  pricing: {
14904
15795
  prompt: computeUsage(`$0.40 / 1M tokens`),
14905
- output: computeUsage(`$0.40 / 1M tokens`), // <- Not sure
15796
+ output: computeUsage(`$0.40 / 1M tokens`),
14906
15797
  },
14907
15798
  },
14908
15799
  /**/
@@ -14956,7 +15847,7 @@ const OPENAI_MODELS = exportJson({
14956
15847
  modelDescription: 'Preview version of GPT-4 Turbo that points to the latest model version. Features improved instruction following, 128K token context window and lower latency.',
14957
15848
  pricing: {
14958
15849
  prompt: computeUsage(`$10.00 / 1M tokens`),
14959
- output: computeUsage(`$30.00 / 1M tokens`), // <- Not sure, just for gpt-4-turbo
15850
+ output: computeUsage(`$30.00 / 1M tokens`),
14960
15851
  },
14961
15852
  },
14962
15853
  /**/
@@ -14967,7 +15858,7 @@ const OPENAI_MODELS = exportJson({
14967
15858
  modelName: 'text-embedding-3-large',
14968
15859
  modelDescription: "OpenAI's most capable text embedding model designed for high-quality embeddings for complex similarity tasks and information retrieval.",
14969
15860
  pricing: {
14970
- prompt: computeUsage(`$0.13 / 1M tokens`),
15861
+ prompt: computeUsage(`$0.13 / 1M tokens`),
14971
15862
  // TODO: [🏏] Leverage the batch API @see https://platform.openai.com/docs/guides/batch
14972
15863
  output: 0, // <- Note: [🆖] In Embedding models you dont pay for output
14973
15864
  },
@@ -15060,8 +15951,8 @@ const OPENAI_MODELS = exportJson({
15060
15951
  modelName: 'gpt-4o-mini',
15061
15952
  modelDescription: 'Smaller, more cost-effective version of GPT-4o with good performance across text, vision, and audio tasks at reduced complexity.',
15062
15953
  pricing: {
15063
- prompt: computeUsage(`$3.00 / 1M tokens`),
15064
- output: computeUsage(`$9.00 / 1M tokens`),
15954
+ prompt: computeUsage(`$0.15 / 1M tokens`),
15955
+ output: computeUsage(`$0.60 / 1M tokens`),
15065
15956
  },
15066
15957
  },
15067
15958
  /**/
@@ -15107,10 +15998,10 @@ const OPENAI_MODELS = exportJson({
15107
15998
  modelVariant: 'CHAT',
15108
15999
  modelTitle: 'o1',
15109
16000
  modelName: 'o1',
16001
+ modelDescription: "OpenAI's advanced reasoning model focused on logic and problem-solving. Designed for complex analytical tasks with rigorous step-by-step reasoning. 128K context window.",
15110
16002
  pricing: {
15111
- prompt: computeUsage(`$3.00 / 1M tokens`),
15112
- output: computeUsage(`$12.00 / 1M tokens`),
15113
- // <- TODO: !! Unsure, check the pricing
16003
+ prompt: computeUsage(`$15.00 / 1M tokens`),
16004
+ output: computeUsage(`$60.00 / 1M tokens`),
15114
16005
  },
15115
16006
  },
15116
16007
  /**/
@@ -15119,7 +16010,7 @@ const OPENAI_MODELS = exportJson({
15119
16010
  modelVariant: 'CHAT',
15120
16011
  modelTitle: 'o3-mini',
15121
16012
  modelName: 'o3-mini',
15122
- modelDescription: 'Compact and efficient reasoning model specializing in problem-solving with a focus on research and analysis tasks.',
16013
+ modelDescription: 'Cost-effective reasoning model optimized for academic and scientific problem-solving. Efficient performance on STEM tasks with deep mathematical and scientific knowledge. 128K context window.',
15123
16014
  pricing: {
15124
16015
  prompt: computeUsage(`$3.00 / 1M tokens`),
15125
16016
  output: computeUsage(`$12.00 / 1M tokens`),
@@ -15186,6 +16077,10 @@ class AzureOpenAiExecutionTools {
15186
16077
  * OpenAI Azure API client.
15187
16078
  */
15188
16079
  this.client = null;
16080
+ // TODO: Allow configuring rate limits via options
16081
+ this.limiter = new Bottleneck({
16082
+ minTime: 60000 / (this.options.maxRequestsPerMinute || DEFAULT_MAX_REQUESTS_PER_MINUTE),
16083
+ });
15189
16084
  }
15190
16085
  get title() {
15191
16086
  return 'Azure OpenAI';
@@ -15263,7 +16158,9 @@ class AzureOpenAiExecutionTools {
15263
16158
  console.info(colors.bgWhite('messages'), JSON.stringify(messages, null, 4));
15264
16159
  }
15265
16160
  const rawRequest = [modelName, messages, modelSettings];
15266
- const rawResponse = await this.withTimeout(client.getChatCompletions(...rawRequest)).catch((error) => {
16161
+ const rawResponse = await this.limiter
16162
+ .schedule(() => this.withTimeout(client.getChatCompletions(...rawRequest)))
16163
+ .catch((error) => {
15267
16164
  if (this.options.isVerbose) {
15268
16165
  console.info(colors.bgRed('error'), error);
15269
16166
  }
@@ -15359,7 +16256,9 @@ class AzureOpenAiExecutionTools {
15359
16256
  [rawPromptContent],
15360
16257
  modelSettings,
15361
16258
  ];
15362
- const rawResponse = await this.withTimeout(client.getCompletions(...rawRequest)).catch((error) => {
16259
+ const rawResponse = await this.limiter
16260
+ .schedule(() => this.withTimeout(client.getCompletions(...rawRequest)))
16261
+ .catch((error) => {
15363
16262
  if (this.options.isVerbose) {
15364
16263
  console.info(colors.bgRed('error'), error);
15365
16264
  }
@@ -15499,9 +16398,11 @@ const _DeepseekMetadataRegistration = $llmToolsMetadataRegister.register({
15499
16398
  packageName: '@promptbook/deepseek',
15500
16399
  className: 'DeepseekExecutionTools',
15501
16400
  envVariables: ['DEEPSEEK_GENERATIVE_AI_API_KEY'],
16401
+ trustLevel: 'UNTRUSTED',
16402
+ order: MODEL_ORDERS.NORMAL,
15502
16403
  getBoilerplateConfiguration() {
15503
16404
  return {
15504
- title: 'Deepseek (boilerplate)',
16405
+ title: 'Deepseek',
15505
16406
  packageName: '@promptbook/deepseek',
15506
16407
  className: 'DeepseekExecutionTools',
15507
16408
  options: {
@@ -15698,6 +16599,67 @@ function createExecutionToolsFromVercelProvider(options) {
15698
16599
  };
15699
16600
  }
15700
16601
 
16602
+ /**
16603
+ * List of available Deepseek models with descriptions
16604
+ *
16605
+ * Note: Done at 2025-05-06
16606
+ *
16607
+ * @see https://www.deepseek.com/models
16608
+ * @public exported from `@promptbook/deepseek`
16609
+ */
16610
+ const DEEPSEEK_MODELS = exportJson({
16611
+ name: 'DEEPSEEK_MODELS',
16612
+ value: [
16613
+ {
16614
+ modelVariant: 'CHAT',
16615
+ modelTitle: 'Deepseek Chat',
16616
+ modelName: 'deepseek-chat',
16617
+ modelDescription: 'General-purpose language model with strong performance across conversation, reasoning, and content generation. 128K context window with excellent instruction following capabilities.',
16618
+ pricing: {
16619
+ prompt: computeUsage(`$0.80 / 1M tokens`),
16620
+ output: computeUsage(`$1.60 / 1M tokens`),
16621
+ },
16622
+ },
16623
+ {
16624
+ modelVariant: 'CHAT',
16625
+ modelTitle: 'Deepseek Reasoner',
16626
+ modelName: 'deepseek-reasoner',
16627
+ modelDescription: 'Specialized model focused on complex reasoning tasks like mathematical problem-solving and logical analysis. Enhanced step-by-step reasoning with explicit chain-of-thought processes. 128K context window.',
16628
+ pricing: {
16629
+ prompt: computeUsage(`$3.50 / 1M tokens`),
16630
+ output: computeUsage(`$7.00 / 1M tokens`),
16631
+ },
16632
+ },
16633
+ {
16634
+ modelVariant: 'CHAT',
16635
+ modelTitle: 'DeepSeek V3',
16636
+ modelName: 'deepseek-v3-0324',
16637
+ modelDescription: 'Advanced general-purpose model with improved reasoning, coding abilities, and multimodal understanding. Built on the latest DeepSeek architecture with enhanced knowledge representation.',
16638
+ pricing: {
16639
+ prompt: computeUsage(`$1.50 / 1M tokens`),
16640
+ output: computeUsage(`$3.00 / 1M tokens`),
16641
+ },
16642
+ },
16643
+ {
16644
+ modelVariant: 'CHAT',
16645
+ modelTitle: 'DeepSeek R1',
16646
+ modelName: 'deepseek-r1',
16647
+ modelDescription: 'Research-focused model optimized for scientific problem-solving and analytical tasks. Excellent performance on tasks requiring domain-specific expertise and critical thinking.',
16648
+ pricing: {
16649
+ prompt: computeUsage(`$5.00 / 1M tokens`),
16650
+ output: computeUsage(`$10.00 / 1M tokens`),
16651
+ },
16652
+ },
16653
+ // <- [🕕]
16654
+ ],
16655
+ });
16656
+ /**
16657
+ * TODO: [🧠] Add information about context window sizes, capabilities, and relative performance characteristics
16658
+ * TODO: [🎰] Some mechanism to auto-update available models
16659
+ * TODO: [🧠] Verify pricing information is current with Deepseek's official documentation
16660
+ * Note: [💞] Ignore a discrepancy between file name and entity name
16661
+ */
16662
+
15701
16663
  /**
15702
16664
  * Execution Tools for calling Deepseek API.
15703
16665
  *
@@ -15719,18 +16681,7 @@ const createDeepseekExecutionTools = Object.assign((options) => {
15719
16681
  title: 'Deepseek',
15720
16682
  description: 'Implementation of Deepseek models',
15721
16683
  vercelProvider: deepseekVercelProvider,
15722
- availableModels: [
15723
- {
15724
- modelName: 'deepseek-chat',
15725
- modelVariant: 'CHAT',
15726
- },
15727
- {
15728
- modelName: 'deepseek-reasoner',
15729
- modelVariant: 'CHAT',
15730
- },
15731
- // <- [🕕]
15732
- // <- TODO: How picking of the default model looks like in `createExecutionToolsFromVercelProvider`
15733
- ],
16684
+ availableModels: DEEPSEEK_MODELS,
15734
16685
  ...options,
15735
16686
  });
15736
16687
  }, {
@@ -15770,9 +16721,11 @@ const _GoogleMetadataRegistration = $llmToolsMetadataRegister.register({
15770
16721
  packageName: '@promptbook/google',
15771
16722
  className: 'GoogleExecutionTools',
15772
16723
  envVariables: ['GOOGLE_GENERATIVE_AI_API_KEY'],
16724
+ trustLevel: 'CLOSED',
16725
+ order: MODEL_ORDERS.NORMAL,
15773
16726
  getBoilerplateConfiguration() {
15774
16727
  return {
15775
- title: 'Google Gemini (boilerplate)',
16728
+ title: 'Google Gemini',
15776
16729
  packageName: '@promptbook/google',
15777
16730
  className: 'GoogleExecutionTools',
15778
16731
  options: {
@@ -15805,6 +16758,173 @@ const _GoogleMetadataRegistration = $llmToolsMetadataRegister.register({
15805
16758
  * Note: [💞] Ignore a discrepancy between file name and entity name
15806
16759
  */
15807
16760
 
16761
+ /**
16762
+ * List of available Google models with descriptions
16763
+ *
16764
+ * Note: Done at 2025-05-06
16765
+ *
16766
+ * @see https://ai.google.dev/models/gemini
16767
+ * @public exported from `@promptbook/google`
16768
+ */
16769
+ const GOOGLE_MODELS = exportJson({
16770
+ name: 'GOOGLE_MODELS',
16771
+ value: [
16772
+ {
16773
+ modelVariant: 'CHAT',
16774
+ modelTitle: 'Gemini 2.5 Pro',
16775
+ modelName: 'gemini-2.5-pro-preview-03-25',
16776
+ modelDescription: 'Latest advanced multimodal model with exceptional reasoning, tool use, and instruction following. 1M token context window with improved vision capabilities for complex visual tasks.',
16777
+ pricing: {
16778
+ prompt: computeUsage(`$8.00 / 1M tokens`),
16779
+ output: computeUsage(`$24.00 / 1M tokens`),
16780
+ },
16781
+ },
16782
+ {
16783
+ modelVariant: 'CHAT',
16784
+ modelTitle: 'Gemini 2.0 Flash',
16785
+ modelName: 'gemini-2.0-flash',
16786
+ modelDescription: 'Fast, efficient model optimized for rapid response times. Good balance between performance and cost, with strong capabilities across text, code, and reasoning tasks. 128K context window.',
16787
+ pricing: {
16788
+ prompt: computeUsage(`$0.35 / 1M tokens`),
16789
+ output: computeUsage(`$1.05 / 1M tokens`),
16790
+ },
16791
+ },
16792
+ {
16793
+ modelVariant: 'CHAT',
16794
+ modelTitle: 'Gemini 2.0 Flash Lite',
16795
+ modelName: 'gemini-2.0-flash-lite',
16796
+ modelDescription: 'Streamlined version of Gemini 2.0 Flash, designed for extremely low-latency applications and edge deployments. Optimized for efficiency while maintaining core capabilities.',
16797
+ pricing: {
16798
+ prompt: computeUsage(`$0.20 / 1M tokens`),
16799
+ output: computeUsage(`$0.60 / 1M tokens`),
16800
+ },
16801
+ },
16802
+ {
16803
+ modelVariant: 'CHAT',
16804
+ modelTitle: 'Gemini 2.0 Flash Thinking',
16805
+ modelName: 'gemini-2.0-flash-thinking-exp-01-21',
16806
+ modelDescription: 'Experimental model focused on enhanced reasoning with explicit chain-of-thought processes. Designed for tasks requiring structured thinking and problem-solving approaches.',
16807
+ pricing: {
16808
+ prompt: computeUsage(`$0.35 / 1M tokens`),
16809
+ output: computeUsage(`$1.05 / 1M tokens`),
16810
+ },
16811
+ },
16812
+ {
16813
+ modelVariant: 'CHAT',
16814
+ modelTitle: 'Gemini 1.5 Flash',
16815
+ modelName: 'gemini-1.5-flash',
16816
+ modelDescription: 'Efficient model balancing speed and quality for general-purpose applications. 1M token context window with good multimodal capabilities and quick response times.',
16817
+ pricing: {
16818
+ prompt: computeUsage(`$0.25 / 1M tokens`),
16819
+ output: computeUsage(`$0.75 / 1M tokens`),
16820
+ },
16821
+ },
16822
+ {
16823
+ modelVariant: 'CHAT',
16824
+ modelTitle: 'Gemini 1.5 Flash Latest',
16825
+ modelName: 'gemini-1.5-flash-latest',
16826
+ modelDescription: 'Points to the latest version of Gemini 1.5 Flash, ensuring access to the most recent improvements and bug fixes while maintaining stable interfaces.',
16827
+ },
16828
+ {
16829
+ modelVariant: 'CHAT',
16830
+ modelTitle: 'Gemini 1.5 Flash 001',
16831
+ modelName: 'gemini-1.5-flash-001',
16832
+ modelDescription: 'First stable release of Gemini 1.5 Flash model with reliable performance characteristics for production applications. 1M token context window.',
16833
+ },
16834
+ {
16835
+ modelVariant: 'CHAT',
16836
+ modelTitle: 'Gemini 1.5 Flash 002',
16837
+ modelName: 'gemini-1.5-flash-002',
16838
+ modelDescription: 'Improved version of Gemini 1.5 Flash with enhanced instruction following and more consistent outputs. Refined for better application integration.',
16839
+ },
16840
+ {
16841
+ modelVariant: 'CHAT',
16842
+ modelTitle: 'Gemini 1.5 Flash Exp',
16843
+ modelName: 'gemini-1.5-flash-exp-0827',
16844
+ modelDescription: 'Experimental version of Gemini 1.5 Flash with new capabilities being tested. May offer improved performance but with potential behavior differences from stable releases.',
16845
+ },
16846
+ {
16847
+ modelVariant: 'CHAT',
16848
+ modelTitle: 'Gemini 1.5 Flash 8B',
16849
+ modelName: 'gemini-1.5-flash-8b',
16850
+ modelDescription: 'Compact 8B parameter model optimized for efficiency and deployment in resource-constrained environments. Good performance despite smaller size.',
16851
+ },
16852
+ {
16853
+ modelVariant: 'CHAT',
16854
+ modelTitle: 'Gemini 1.5 Flash 8B Latest',
16855
+ modelName: 'gemini-1.5-flash-8b-latest',
16856
+ modelDescription: 'Points to the most recent version of the compact 8B parameter model, providing latest improvements while maintaining a small footprint.',
16857
+ },
16858
+ {
16859
+ modelVariant: 'CHAT',
16860
+ modelTitle: 'Gemini 1.5 Flash 8B Exp',
16861
+ modelName: 'gemini-1.5-flash-8b-exp-0924',
16862
+ modelDescription: 'Experimental version of the 8B parameter model with new capabilities and optimizations being evaluated for future stable releases.',
16863
+ },
16864
+ {
16865
+ modelVariant: 'CHAT',
16866
+ modelTitle: 'Gemini 1.5 Flash 8B Exp',
16867
+ modelName: 'gemini-1.5-flash-8b-exp-0827',
16868
+ modelDescription: 'August experimental release of the efficient 8B parameter model with specific improvements to reasoning capabilities and response quality.',
16869
+ },
16870
+ {
16871
+ modelVariant: 'CHAT',
16872
+ modelTitle: 'Gemini 1.5 Pro Latest',
16873
+ modelName: 'gemini-1.5-pro-latest',
16874
+ modelDescription: 'Points to the most recent version of the flagship Gemini 1.5 Pro model, ensuring access to the latest capabilities and improvements.',
16875
+ pricing: {
16876
+ prompt: computeUsage(`$7.00 / 1M tokens`),
16877
+ output: computeUsage(`$21.00 / 1M tokens`),
16878
+ },
16879
+ },
16880
+ {
16881
+ modelVariant: 'CHAT',
16882
+ modelTitle: 'Gemini 1.5 Pro',
16883
+ modelName: 'gemini-1.5-pro',
16884
+ modelDescription: 'Flagship multimodal model with strong performance across text, code, vision, and audio tasks. 1M token context window with excellent reasoning capabilities.',
16885
+ pricing: {
16886
+ prompt: computeUsage(`$6.00 / 1M tokens`),
16887
+ output: computeUsage(`$18.00 / 1M tokens`),
16888
+ },
16889
+ },
16890
+ {
16891
+ modelVariant: 'CHAT',
16892
+ modelTitle: 'Gemini 1.5 Pro 001',
16893
+ modelName: 'gemini-1.5-pro-001',
16894
+ modelDescription: 'First stable release of Gemini 1.5 Pro with consistent performance characteristics and reliable behavior for production applications.',
16895
+ },
16896
+ {
16897
+ modelVariant: 'CHAT',
16898
+ modelTitle: 'Gemini 1.5 Pro 002',
16899
+ modelName: 'gemini-1.5-pro-002',
16900
+ modelDescription: 'Refined version of Gemini 1.5 Pro with improved instruction following, better multimodal understanding, and more consistent outputs.',
16901
+ },
16902
+ {
16903
+ modelVariant: 'CHAT',
16904
+ modelTitle: 'Gemini 1.5 Pro Exp',
16905
+ modelName: 'gemini-1.5-pro-exp-0827',
16906
+ modelDescription: 'Experimental version of Gemini 1.5 Pro with new capabilities and optimizations being tested before wider release. May offer improved performance.',
16907
+ },
16908
+ {
16909
+ modelVariant: 'CHAT',
16910
+ modelTitle: 'Gemini 1.0 Pro',
16911
+ modelName: 'gemini-1.0-pro',
16912
+ modelDescription: 'Original Gemini series foundation model with solid multimodal capabilities. 32K context window with good performance on text, code, and basic vision tasks.',
16913
+ pricing: {
16914
+ prompt: computeUsage(`$0.35 / 1M tokens`),
16915
+ output: computeUsage(`$1.05 / 1M tokens`),
16916
+ },
16917
+ },
16918
+ // <- [🕕]
16919
+ ],
16920
+ });
16921
+ /**
16922
+ * TODO: [🧠] Add information about context window sizes, capabilities, and relative performance characteristics
16923
+ * TODO: [🎰] Some mechanism to auto-update available models
16924
+ * TODO: [🧠] Verify pricing information is current with Google's official documentation
16925
+ * Note: [💞] Ignore a discrepancy between file name and entity name
16926
+ */
16927
+
15808
16928
  /**
15809
16929
  * Execution Tools for calling Google Gemini API.
15810
16930
  *
@@ -15826,29 +16946,7 @@ const createGoogleExecutionTools = Object.assign((options) => {
15826
16946
  title: 'Google',
15827
16947
  description: 'Implementation of Google models',
15828
16948
  vercelProvider: googleGeminiVercelProvider,
15829
- availableModels: [
15830
- // TODO: [🕘] Maybe list models in same way as in other providers - in separate file with metadata
15831
- 'gemini-2.5-pro-preview-03-25',
15832
- 'gemini-2.0-flash',
15833
- 'gemini-2.0-flash-lite',
15834
- 'gemini-2.0-flash-thinking-exp-01-21',
15835
- 'gemini-1.5-flash',
15836
- 'gemini-1.5-flash-latest',
15837
- 'gemini-1.5-flash-001',
15838
- 'gemini-1.5-flash-002',
15839
- 'gemini-1.5-flash-exp-0827',
15840
- 'gemini-1.5-flash-8b',
15841
- 'gemini-1.5-flash-8b-latest',
15842
- 'gemini-1.5-flash-8b-exp-0924',
15843
- 'gemini-1.5-flash-8b-exp-0827',
15844
- 'gemini-1.5-pro-latest',
15845
- 'gemini-1.5-pro',
15846
- 'gemini-1.5-pro-001',
15847
- 'gemini-1.5-pro-002',
15848
- 'gemini-1.5-pro-exp-0827',
15849
- 'gemini-1.0-pro',
15850
- // <- [🕕]
15851
- ].map((modelName) => ({ modelName, modelVariant: 'CHAT' })),
16949
+ availableModels: GOOGLE_MODELS,
15852
16950
  ...options,
15853
16951
  });
15854
16952
  }, {
@@ -15888,13 +16986,16 @@ const _OpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
15888
16986
  packageName: '@promptbook/openai',
15889
16987
  className: 'OpenAiExecutionTools',
15890
16988
  envVariables: ['OPENAI_API_KEY'],
16989
+ trustLevel: 'CLOSED',
16990
+ order: MODEL_ORDERS.TOP_TIER,
15891
16991
  getBoilerplateConfiguration() {
15892
16992
  return {
15893
- title: 'Open AI (boilerplate)',
16993
+ title: 'Open AI',
15894
16994
  packageName: '@promptbook/openai',
15895
16995
  className: 'OpenAiExecutionTools',
15896
16996
  options: {
15897
16997
  apiKey: 'sk-',
16998
+ maxRequestsPerMinute: DEFAULT_MAX_REQUESTS_PER_MINUTE,
15898
16999
  },
15899
17000
  };
15900
17001
  },
@@ -15914,9 +17015,9 @@ const _OpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
15914
17015
  },
15915
17016
  });
15916
17017
  /**
15917
- * @@@ registration1 of default configuration for Open AI
17018
+ * Registration of the OpenAI Assistant metadata
15918
17019
  *
15919
- * Note: [🏐] Configurations registrations are done in @@@ BUT constructor @@@
17020
+ * Note: [🏐] Configurations registrations are done in the metadata registration section, but the constructor registration is handled separately.
15920
17021
  *
15921
17022
  * @public exported from `@promptbook/core`
15922
17023
  * @public exported from `@promptbook/wizzard`
@@ -15928,9 +17029,11 @@ const _OpenAiAssistantMetadataRegistration = $llmToolsMetadataRegister.register(
15928
17029
  className: 'OpenAiAssistantExecutionTools',
15929
17030
  envVariables: null,
15930
17031
  // <- TODO: ['OPENAI_API_KEY', 'OPENAI_ASSISTANT_ID']
17032
+ trustLevel: 'CLOSED',
17033
+ order: MODEL_ORDERS.NORMAL,
15931
17034
  getBoilerplateConfiguration() {
15932
17035
  return {
15933
- title: 'Open AI Assistant (boilerplate)',
17036
+ title: 'Open AI Assistant',
15934
17037
  packageName: '@promptbook/openai',
15935
17038
  className: 'OpenAiAssistantExecutionTools',
15936
17039
  options: {
@@ -15975,20 +17078,39 @@ function computeOpenAiUsage(promptContent, // <- Note: Intentionally using [] to
15975
17078
  resultContent, rawResponse) {
15976
17079
  var _a, _b;
15977
17080
  if (rawResponse.usage === undefined) {
17081
+ console.log('!!! computeOpenAiUsage', 'The usage is not defined in the response from OpenAI');
15978
17082
  throw new PipelineExecutionError('The usage is not defined in the response from OpenAI');
15979
17083
  }
15980
17084
  if (((_a = rawResponse.usage) === null || _a === void 0 ? void 0 : _a.prompt_tokens) === undefined) {
17085
+ console.log('!!! computeOpenAiUsage', 'In OpenAI response `usage.prompt_tokens` not defined');
15981
17086
  throw new PipelineExecutionError('In OpenAI response `usage.prompt_tokens` not defined');
15982
17087
  }
15983
17088
  const inputTokens = rawResponse.usage.prompt_tokens;
15984
17089
  const outputTokens = ((_b = rawResponse.usage) === null || _b === void 0 ? void 0 : _b.completion_tokens) || 0;
15985
- const modelInfo = OPENAI_MODELS.find((model) => model.modelName === rawResponse.model);
17090
+ let isUncertain = false;
17091
+ let modelInfo = OPENAI_MODELS.find((model) => model.modelName === rawResponse.model);
17092
+ if (modelInfo === undefined) {
17093
+ // Note: Model is not in the list of known models, fallback to the family of the models and mark price as uncertain
17094
+ modelInfo = OPENAI_MODELS.find((model) => (rawResponse.model || SALT_NONCE).startsWith(model.modelName));
17095
+ if (modelInfo !== undefined) {
17096
+ isUncertain = true;
17097
+ }
17098
+ }
17099
+ console.log('!!! computeOpenAiUsage', {
17100
+ inputTokens,
17101
+ outputTokens,
17102
+ rawResponse,
17103
+ 'rawResponse.model': rawResponse.model,
17104
+ OPENAI_MODELS,
17105
+ resultContent,
17106
+ modelInfo,
17107
+ });
15986
17108
  let price;
15987
17109
  if (modelInfo === undefined || modelInfo.pricing === undefined) {
15988
17110
  price = uncertainNumber();
15989
17111
  }
15990
17112
  else {
15991
- price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output);
17113
+ price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output, isUncertain);
15992
17114
  }
15993
17115
  return {
15994
17116
  price,
@@ -16023,6 +17145,10 @@ class OpenAiExecutionTools {
16023
17145
  * OpenAI API client.
16024
17146
  */
16025
17147
  this.client = null;
17148
+ // TODO: Allow configuring rate limits via options
17149
+ this.limiter = new Bottleneck({
17150
+ minTime: 60000 / (this.options.maxRequestsPerMinute || DEFAULT_MAX_REQUESTS_PER_MINUTE),
17151
+ });
16026
17152
  }
16027
17153
  get title() {
16028
17154
  return 'OpenAI';
@@ -16126,7 +17252,9 @@ class OpenAiExecutionTools {
16126
17252
  if (this.options.isVerbose) {
16127
17253
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
16128
17254
  }
16129
- const rawResponse = await client.chat.completions.create(rawRequest).catch((error) => {
17255
+ const rawResponse = await this.limiter
17256
+ .schedule(() => client.chat.completions.create(rawRequest))
17257
+ .catch((error) => {
16130
17258
  assertsError(error);
16131
17259
  if (this.options.isVerbose) {
16132
17260
  console.info(colors.bgRed('error'), error);
@@ -16203,7 +17331,9 @@ class OpenAiExecutionTools {
16203
17331
  if (this.options.isVerbose) {
16204
17332
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
16205
17333
  }
16206
- const rawResponse = await client.completions.create(rawRequest).catch((error) => {
17334
+ const rawResponse = await this.limiter
17335
+ .schedule(() => client.completions.create(rawRequest))
17336
+ .catch((error) => {
16207
17337
  assertsError(error);
16208
17338
  if (this.options.isVerbose) {
16209
17339
  console.info(colors.bgRed('error'), error);
@@ -16267,7 +17397,9 @@ class OpenAiExecutionTools {
16267
17397
  if (this.options.isVerbose) {
16268
17398
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
16269
17399
  }
16270
- const rawResponse = await client.embeddings.create(rawRequest).catch((error) => {
17400
+ const rawResponse = await this.limiter
17401
+ .schedule(() => client.embeddings.create(rawRequest))
17402
+ .catch((error) => {
16271
17403
  assertsError(error);
16272
17404
  if (this.options.isVerbose) {
16273
17405
  console.info(colors.bgRed('error'), error);
@@ -16365,6 +17497,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
16365
17497
  constructor(options) {
16366
17498
  super(options);
16367
17499
  this.assistantId = options.assistantId;
17500
+ // TODO: [👱] Make limiter same as in `OpenAiExecutionTools`
16368
17501
  }
16369
17502
  get title() {
16370
17503
  return 'OpenAI Assistant';
@@ -16566,9 +17699,9 @@ const createOpenAiExecutionTools = Object.assign((options) => {
16566
17699
  */
16567
17700
  const _OpenAiRegistration = $llmToolsRegister.register(createOpenAiExecutionTools);
16568
17701
  /**
16569
- * @@@ registration2
17702
+ * Registration of the OpenAI Assistant provider
16570
17703
  *
16571
- * Note: [🏐] Configurations registrations are done in @@@ BUT constructor @@@
17704
+ * Note: [🏐] Configurations registrations are done in register-constructor.ts BUT constructor register-constructor.ts
16572
17705
  *
16573
17706
  * @public exported from `@promptbook/openai`
16574
17707
  * @public exported from `@promptbook/wizzard`
@@ -16581,9 +17714,8 @@ const _OpenAiAssistantRegistration = $llmToolsRegister.register(createOpenAiAssi
16581
17714
  */
16582
17715
 
16583
17716
  /**
16584
- * Create a filename for intermediate cache for scrapers
16585
- *
16586
- * Note: It also checks if directory exists and creates it if not
17717
+ * Retrieves an intermediate source for a scraper based on the knowledge source.
17718
+ * Manages the caching and retrieval of intermediate scraper results for optimized performance.
16587
17719
  *
16588
17720
  * @private as internal utility for scrapers
16589
17721
  */
@@ -16810,14 +17942,14 @@ const boilerplateScraperMetadata = $deepFreeze({
16810
17942
  packageName: '@promptbook/boilerplate',
16811
17943
  className: 'BoilerplateScraper',
16812
17944
  mimeTypes: [
16813
- '@@@/@@@',
16814
- // <- TODO: @@@ Add compatible mime types with Boilerplate scraper
17945
+ '@@/@@',
17946
+ // <- TODO: @@ Add compatible mime types with Boilerplate scraper
16815
17947
  ],
16816
- documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@@',
17948
+ documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
16817
17949
  isAvilableInBrowser: false,
16818
17950
  // <- Note: [🌏] Only `MarkdownScraper` makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
16819
17951
  requiredExecutables: [
16820
- /* @@@ 'Pandoc' */
17952
+ /* @@ 'Pandoc' */
16821
17953
  ],
16822
17954
  }); /* <- Note: [🤛] */
16823
17955
  /**
@@ -16835,7 +17967,7 @@ const _BoilerplateScraperMetadataRegistration = $scrapersMetadataRegister.regist
16835
17967
  */
16836
17968
 
16837
17969
  /**
16838
- * Scraper of @@@ files
17970
+ * Scraper of @@ files
16839
17971
  *
16840
17972
  * @see `documentationUrl` for more details
16841
17973
  * @public exported from `@promptbook/boilerplate`
@@ -16853,30 +17985,30 @@ class BoilerplateScraper {
16853
17985
  this.markdownScraper = new MarkdownScraper(tools, options);
16854
17986
  }
16855
17987
  /**
16856
- * Convert the `.@@@` to `.md` file and returns intermediate source
17988
+ * Convert the `.@@` to `.md` file and returns intermediate source
16857
17989
  *
16858
17990
  * Note: `$` is used to indicate that this function is not a pure function - it leaves files on the disk and you are responsible for cleaning them by calling `destroy` method of returned object
16859
17991
  */
16860
17992
  async $convert(source) {
16861
17993
  var _a;
16862
17994
  const { rootDirname = process.cwd(), cacheDirname = DEFAULT_SCRAPE_CACHE_DIRNAME, intermediateFilesStrategy = DEFAULT_INTERMEDIATE_FILES_STRATEGY, isVerbose = DEFAULT_IS_VERBOSE, } = this.options;
16863
- // TODO: @@@ Preserve or delete
17995
+ // TODO: @@ Preserve or delete
16864
17996
  if (!$isRunningInNode()) {
16865
17997
  throw new KnowledgeScrapeError('BoilerplateScraper is only supported in Node environment');
16866
17998
  }
16867
- // TODO: @@@ Preserve or delete
17999
+ // TODO: @@ Preserve or delete
16868
18000
  if (this.tools.fs === undefined) {
16869
18001
  throw new EnvironmentMismatchError('Can not scrape boilerplates without filesystem tools');
16870
18002
  // <- TODO: [🧠] What is the best error type here`
16871
18003
  }
16872
- // TODO: @@@ Preserve, delete or modify
18004
+ // TODO: @@ Preserve, delete or modify
16873
18005
  if (((_a = this.tools.executables) === null || _a === void 0 ? void 0 : _a.pandocPath) === undefined) {
16874
18006
  throw new MissingToolsError('Pandoc is required for scraping .docx files');
16875
18007
  }
16876
- // TODO: @@@ Preserve, delete or modify
18008
+ // TODO: @@ Preserve, delete or modify
16877
18009
  if (source.filename === null) {
16878
18010
  // TODO: [🧠] Maybe save file as temporary
16879
- throw new KnowledgeScrapeError('When parsing .@@@ file, it must be real file in the file system');
18011
+ throw new KnowledgeScrapeError('When parsing .@@ file, it must be real file in the file system');
16880
18012
  }
16881
18013
  const extension = getFileExtension(source.filename);
16882
18014
  const cacheFilehandler = await getScraperIntermediateSource(source, {
@@ -16886,7 +18018,7 @@ class BoilerplateScraper {
16886
18018
  extension: 'md',
16887
18019
  isVerbose,
16888
18020
  });
16889
- // TODO: @@@ Preserve, delete or modify
18021
+ // TODO: @@ Preserve, delete or modify
16890
18022
  // Note: Running Pandoc ONLY if the file in the cache does not exist
16891
18023
  if (!(await isFileExisting(cacheFilehandler.filename, this.tools.fs))) {
16892
18024
  const command = `"${this.tools.executables.pandocPath}" -f ${extension} -t markdown "${source.filename}" -o "${cacheFilehandler.filename}"`;
@@ -16912,7 +18044,7 @@ class BoilerplateScraper {
16912
18044
  */
16913
18045
  async scrape(source) {
16914
18046
  const cacheFilehandler = await this.$convert(source);
16915
- // TODO: @@@ Preserve, delete or modify
18047
+ // TODO: @@ Preserve, delete or modify
16916
18048
  const markdownSource = {
16917
18049
  source: source.source,
16918
18050
  filename: cacheFilehandler.filename,
@@ -16943,7 +18075,7 @@ class BoilerplateScraper {
16943
18075
  * TODO: [👣] Converted documents can act as cached items - there is no need to run conversion each time
16944
18076
  * TODO: [🪂] Do it in parallel
16945
18077
  * Note: No need to aggregate usage here, it is done by intercepting the llmTools
16946
- * @@@ Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
18078
+ * @@ Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
16947
18079
  */
16948
18080
 
16949
18081
  /**
@@ -17277,7 +18409,8 @@ class LegacyDocumentScraper {
17277
18409
  */
17278
18410
 
17279
18411
  /**
17280
- * @@@
18412
+ * Creates a scraper for legacy document formats (.doc, .rtf, etc).
18413
+ * Uses LibreOffice for conversion to extract content from older document formats.
17281
18414
  *
17282
18415
  * @public exported from `@promptbook/legacy-documents`
17283
18416
  */
@@ -17304,7 +18437,7 @@ const _LegacyDocumentScraperRegistration = $scrapersRegister.register(createLega
17304
18437
  */
17305
18438
 
17306
18439
  /**
17307
- * @@@
18440
+ * Creates a scraper for document content.
17308
18441
  *
17309
18442
  * @public exported from `@promptbook/documents`
17310
18443
  */
@@ -17331,7 +18464,7 @@ const _DocumentScraperRegistration = $scrapersRegister.register(createDocumentSc
17331
18464
  */
17332
18465
 
17333
18466
  /**
17334
- * @@@
18467
+ * Creates a scraper for markdown content.
17335
18468
  *
17336
18469
  * @public exported from `@promptbook/markdown-utils`
17337
18470
  */
@@ -17437,8 +18570,8 @@ class MarkitdownScraper {
17437
18570
  extension: 'md',
17438
18571
  isVerbose,
17439
18572
  });
17440
- // TODO: @@@ Preserve, delete or modify
17441
- // Note: Running Pandoc ONLY if the file in the cache does not exist
18573
+ // TODO: Determine if Markitdown conversion should run only if the cache file doesn't exist, or always.
18574
+ // Note: Running Markitdown conversion ONLY if the file in the cache does not exist
17442
18575
  if (!(await isFileExisting(cacheFilehandler.filename, this.tools.fs))) {
17443
18576
  const src = source.filename || source.url || null;
17444
18577
  // console.log('!!', { src, source, cacheFilehandler });
@@ -17460,11 +18593,11 @@ class MarkitdownScraper {
17460
18593
  return cacheFilehandler;
17461
18594
  }
17462
18595
  /**
17463
- * Scrapes the docx file and returns the knowledge pieces or `null` if it can't scrape it
18596
+ * Scrapes the source document (PDF, DOCX, etc.) and returns the knowledge pieces or `null` if it can't scrape it.
17464
18597
  */
17465
18598
  async scrape(source) {
17466
18599
  const cacheFilehandler = await this.$convert(source);
17467
- // TODO: @@@ Preserve, delete or modify
18600
+ // TODO: Ensure this correctly creates the source object for the internal MarkdownScraper using the converted file.
17468
18601
  const markdownSource = {
17469
18602
  source: source.source,
17470
18603
  filename: cacheFilehandler.filename,
@@ -17608,7 +18741,8 @@ class PdfScraper {
17608
18741
  */
17609
18742
 
17610
18743
  /**
17611
- * @@@
18744
+ * Factory function to create an instance of PdfScraper.
18745
+ * It bundles the scraper class with its metadata.
17612
18746
  *
17613
18747
  * @public exported from `@promptbook/pdf`
17614
18748
  */
@@ -17784,7 +18918,8 @@ class WebsiteScraper {
17784
18918
  */
17785
18919
 
17786
18920
  /**
17787
- * @@@
18921
+ * Factory function to create an instance of WebsiteScraper.
18922
+ * It bundles the scraper class with its metadata.
17788
18923
  *
17789
18924
  * @public exported from `@promptbook/website-crawler`
17790
18925
  */