@promptbook/cli 0.92.0-3 → 0.92.0-30

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 (171) hide show
  1. package/esm/index.es.js +1589 -460
  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/platforms/locateAppOnLinux.d.ts +2 -1
  25. package/esm/typings/src/executables/platforms/locateAppOnMacOs.d.ts +2 -1
  26. package/esm/typings/src/executables/platforms/locateAppOnWindows.d.ts +2 -1
  27. package/esm/typings/src/execution/AbstractTaskResult.d.ts +1 -1
  28. package/esm/typings/src/execution/CommonToolsOptions.d.ts +5 -1
  29. package/esm/typings/src/execution/LlmExecutionToolsConstructor.d.ts +2 -1
  30. package/esm/typings/src/execution/PipelineExecutorResult.d.ts +4 -2
  31. package/esm/typings/src/execution/createPipelineExecutor/$OngoingTaskResult.d.ts +12 -9
  32. package/esm/typings/src/execution/createPipelineExecutor/10-executePipeline.d.ts +12 -9
  33. package/esm/typings/src/execution/createPipelineExecutor/20-executeTask.d.ts +11 -8
  34. package/esm/typings/src/execution/createPipelineExecutor/30-executeFormatSubvalues.d.ts +15 -3
  35. package/esm/typings/src/execution/createPipelineExecutor/40-executeAttempts.d.ts +20 -14
  36. package/esm/typings/src/execution/createPipelineExecutor/computeCosineSimilarity.d.ts +13 -0
  37. package/esm/typings/src/execution/createPipelineExecutor/filterJustOutputParameters.d.ts +7 -6
  38. package/esm/typings/src/execution/createPipelineExecutor/getContextForTask.d.ts +5 -1
  39. package/esm/typings/src/execution/createPipelineExecutor/getExamplesForTask.d.ts +1 -1
  40. package/esm/typings/src/execution/createPipelineExecutor/getKnowledgeForTask.d.ts +21 -5
  41. package/esm/typings/src/execution/createPipelineExecutor/getReservedParametersForTask.d.ts +19 -5
  42. package/esm/typings/src/execution/createPipelineExecutor/knowledgePiecesToString.d.ts +9 -0
  43. package/esm/typings/src/execution/translation/automatic-translate/automatic-translators/LindatAutomaticTranslator.d.ts +4 -4
  44. package/esm/typings/src/execution/utils/checkExpectations.d.ts +1 -1
  45. package/esm/typings/src/execution/utils/uncertainNumber.d.ts +3 -2
  46. package/esm/typings/src/formats/_common/{FormatDefinition.d.ts → FormatParser.d.ts} +8 -6
  47. package/esm/typings/src/formats/_common/FormatSubvalueParser.d.ts +66 -0
  48. package/esm/typings/src/formats/csv/CsvFormatParser.d.ts +17 -0
  49. package/esm/typings/src/formats/csv/CsvSettings.d.ts +2 -2
  50. package/esm/typings/src/formats/csv/utils/csvParse.d.ts +12 -0
  51. package/esm/typings/src/formats/csv/utils/isValidCsvString.d.ts +1 -1
  52. package/esm/typings/src/formats/index.d.ts +2 -2
  53. package/esm/typings/src/formats/json/{JsonFormatDefinition.d.ts → JsonFormatParser.d.ts} +6 -6
  54. package/esm/typings/src/formats/json/utils/isValidJsonString.d.ts +1 -1
  55. package/esm/typings/src/formats/json/utils/jsonParse.d.ts +8 -0
  56. package/esm/typings/src/formats/text/{TextFormatDefinition.d.ts → TextFormatParser.d.ts} +7 -7
  57. package/esm/typings/src/formats/xml/XmlFormatParser.d.ts +19 -0
  58. package/esm/typings/src/formats/xml/utils/isValidXmlString.d.ts +1 -1
  59. package/esm/typings/src/formfactors/_boilerplate/BoilerplateFormfactorDefinition.d.ts +3 -2
  60. package/esm/typings/src/formfactors/_common/AbstractFormfactorDefinition.d.ts +16 -7
  61. package/esm/typings/src/formfactors/_common/FormfactorDefinition.d.ts +3 -1
  62. package/esm/typings/src/formfactors/_common/string_formfactor_name.d.ts +2 -1
  63. package/esm/typings/src/formfactors/chatbot/ChatbotFormfactorDefinition.d.ts +2 -2
  64. package/esm/typings/src/formfactors/completion/CompletionFormfactorDefinition.d.ts +29 -0
  65. package/esm/typings/src/formfactors/generator/GeneratorFormfactorDefinition.d.ts +2 -1
  66. package/esm/typings/src/formfactors/generic/GenericFormfactorDefinition.d.ts +2 -2
  67. package/esm/typings/src/formfactors/index.d.ts +33 -8
  68. package/esm/typings/src/formfactors/matcher/MatcherFormfactorDefinition.d.ts +4 -2
  69. package/esm/typings/src/formfactors/sheets/SheetsFormfactorDefinition.d.ts +3 -2
  70. package/esm/typings/src/formfactors/translator/TranslatorFormfactorDefinition.d.ts +3 -2
  71. package/esm/typings/src/high-level-abstractions/index.d.ts +2 -2
  72. package/esm/typings/src/llm-providers/_common/register/$llmToolsMetadataRegister.d.ts +3 -3
  73. package/esm/typings/src/llm-providers/_common/register/$llmToolsRegister.d.ts +3 -3
  74. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +4 -4
  75. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +4 -3
  76. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +17 -4
  77. package/esm/typings/src/llm-providers/_common/register/LlmToolsConfiguration.d.ts +11 -4
  78. package/esm/typings/src/llm-providers/_common/register/LlmToolsMetadata.d.ts +27 -5
  79. package/esm/typings/src/llm-providers/_common/register/LlmToolsOptions.d.ts +9 -2
  80. package/esm/typings/src/llm-providers/_common/register/createLlmToolsFromConfiguration.d.ts +12 -3
  81. package/esm/typings/src/llm-providers/_common/utils/cache/CacheItem.d.ts +10 -5
  82. package/esm/typings/src/llm-providers/_common/utils/cache/CacheLlmToolsOptions.d.ts +5 -3
  83. package/esm/typings/src/llm-providers/_common/utils/cache/cacheLlmTools.d.ts +3 -3
  84. package/esm/typings/src/llm-providers/_common/utils/count-total-usage/limitTotalUsage.d.ts +5 -5
  85. package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  86. package/esm/typings/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +4 -0
  87. package/esm/typings/src/llm-providers/deepseek/deepseek-models.d.ts +23 -0
  88. package/esm/typings/src/llm-providers/google/google-models.d.ts +23 -0
  89. package/esm/typings/src/llm-providers/openai/OpenAiExecutionTools.d.ts +4 -0
  90. package/esm/typings/src/llm-providers/openai/openai-models.d.ts +1 -1
  91. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +2 -2
  92. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +2 -2
  93. package/esm/typings/src/migrations/migratePipeline.d.ts +9 -0
  94. package/esm/typings/src/other/templates/getBookTemplates.d.ts +2 -2
  95. package/esm/typings/src/personas/preparePersona.d.ts +1 -1
  96. package/esm/typings/src/pipeline/PipelineInterface/PipelineInterface.d.ts +3 -3
  97. package/esm/typings/src/pipeline/PipelineInterface/constants.d.ts +1 -1
  98. package/esm/typings/src/pipeline/PipelineInterface/getPipelineInterface.d.ts +1 -1
  99. package/esm/typings/src/pipeline/PipelineInterface/isPipelineImplementingInterface.d.ts +5 -4
  100. package/esm/typings/src/pipeline/PipelineInterface/isPipelineInterfacesEqual.d.ts +1 -1
  101. package/esm/typings/src/pipeline/PipelineJson/CommonTaskJson.d.ts +9 -6
  102. package/esm/typings/src/pipeline/PipelineJson/PersonaJson.d.ts +4 -2
  103. package/esm/typings/src/pipeline/PipelineJson/PipelineJson.d.ts +3 -2
  104. package/esm/typings/src/pipeline/PipelineString.d.ts +3 -1
  105. package/esm/typings/src/pipeline/book-notation.d.ts +2 -2
  106. package/esm/typings/src/postprocessing/utils/extractJsonBlock.d.ts +1 -1
  107. package/esm/typings/src/prepare/prepareTasks.d.ts +7 -4
  108. package/esm/typings/src/remote-server/openapi-types.d.ts +348 -6
  109. package/esm/typings/src/remote-server/openapi.d.ts +398 -4
  110. package/esm/typings/src/remote-server/types/RemoteServerOptions.d.ts +2 -1
  111. package/esm/typings/src/scrapers/_boilerplate/BoilerplateScraper.d.ts +3 -3
  112. package/esm/typings/src/scrapers/_boilerplate/createBoilerplateScraper.d.ts +1 -1
  113. package/esm/typings/src/scrapers/_boilerplate/register-metadata.d.ts +1 -1
  114. package/esm/typings/src/scrapers/_common/Converter.d.ts +3 -1
  115. package/esm/typings/src/scrapers/_common/Scraper.d.ts +4 -3
  116. package/esm/typings/src/scrapers/_common/ScraperIntermediateSource.d.ts +4 -2
  117. package/esm/typings/src/scrapers/_common/register/$provideFilesystemForNode.d.ts +2 -1
  118. package/esm/typings/src/scrapers/_common/register/$provideScrapersForBrowser.d.ts +6 -3
  119. package/esm/typings/src/scrapers/_common/register/$provideScrapersForNode.d.ts +3 -5
  120. package/esm/typings/src/scrapers/_common/register/$scrapersMetadataRegister.d.ts +3 -3
  121. package/esm/typings/src/scrapers/_common/register/$scrapersRegister.d.ts +3 -2
  122. package/esm/typings/src/scrapers/_common/register/ScraperAndConverterMetadata.d.ts +8 -5
  123. package/esm/typings/src/scrapers/_common/register/ScraperConstructor.d.ts +2 -1
  124. package/esm/typings/src/scrapers/_common/utils/getScraperIntermediateSource.d.ts +6 -5
  125. package/esm/typings/src/scrapers/_common/utils/makeKnowledgeSourceHandler.d.ts +3 -1
  126. package/esm/typings/src/scrapers/document/createDocumentScraper.d.ts +1 -1
  127. package/esm/typings/src/scrapers/document-legacy/createLegacyDocumentScraper.d.ts +2 -1
  128. package/esm/typings/src/scrapers/markdown/createMarkdownScraper.d.ts +4 -1
  129. package/esm/typings/src/scrapers/markitdown/MarkitdownScraper.d.ts +1 -1
  130. package/esm/typings/src/scrapers/pdf/createPdfScraper.d.ts +2 -1
  131. package/esm/typings/src/scrapers/website/createWebsiteScraper.d.ts +3 -4
  132. package/esm/typings/src/scripting/javascript/postprocessing-functions.d.ts +5 -1
  133. package/esm/typings/src/storage/file-cache-storage/FileCacheStorage.d.ts +12 -5
  134. package/esm/typings/src/storage/file-cache-storage/FileCacheStorageOptions.d.ts +4 -2
  135. package/esm/typings/src/storage/file-cache-storage/utils/nameToSubfolderPath.d.ts +2 -1
  136. package/esm/typings/src/storage/local-storage/getIndexedDbStorage.d.ts +10 -0
  137. package/esm/typings/src/storage/local-storage/utils/makePromptbookStorageFromIndexedDb.d.ts +7 -0
  138. package/esm/typings/src/storage/local-storage/utils/makePromptbookStorageFromWebStorage.d.ts +2 -1
  139. package/esm/typings/src/types/IntermediateFilesStrategy.d.ts +2 -1
  140. package/esm/typings/src/types/ModelVariant.d.ts +5 -5
  141. package/esm/typings/src/types/typeAliases.d.ts +17 -13
  142. package/esm/typings/src/utils/$Register.d.ts +8 -7
  143. package/esm/typings/src/utils/editable/edit-pipeline-string/addPipelineCommand.d.ts +2 -2
  144. package/esm/typings/src/utils/editable/edit-pipeline-string/deflatePipeline.d.ts +4 -1
  145. package/esm/typings/src/utils/editable/utils/isFlatPipeline.d.ts +2 -1
  146. package/esm/typings/src/utils/environment/$getGlobalScope.d.ts +2 -1
  147. package/esm/typings/src/utils/expectation-counters/index.d.ts +1 -1
  148. package/esm/typings/src/utils/markdown/extractAllListItemsFromMarkdown.d.ts +1 -1
  149. package/esm/typings/src/utils/normalization/nameToUriPart.d.ts +4 -4
  150. package/esm/typings/src/utils/normalization/nameToUriParts.d.ts +4 -4
  151. package/esm/typings/src/utils/normalization/normalize-to-kebab-case.d.ts +3 -3
  152. package/esm/typings/src/utils/normalization/normalizeTo_SCREAMING_CASE.d.ts +3 -3
  153. package/esm/typings/src/utils/normalization/normalizeTo_camelCase.d.ts +4 -4
  154. package/esm/typings/src/utils/normalization/normalizeTo_snake_case.d.ts +3 -3
  155. package/esm/typings/src/utils/normalization/removeDiacritics.d.ts +3 -3
  156. package/esm/typings/src/utils/normalization/searchKeywords.d.ts +4 -1
  157. package/esm/typings/src/utils/normalization/titleToName.d.ts +4 -4
  158. package/esm/typings/src/utils/organization/empty_object.d.ts +2 -2
  159. package/esm/typings/src/utils/organization/just_empty_object.d.ts +4 -4
  160. package/esm/typings/src/utils/parameters/mapAvailableToExpectedParameters.d.ts +7 -7
  161. package/esm/typings/src/utils/serialization/clonePipeline.d.ts +4 -3
  162. package/esm/typings/src/utils/serialization/deepClone.d.ts +5 -1
  163. package/esm/typings/src/utils/validators/javascriptName/isValidJavascriptName.d.ts +3 -3
  164. package/esm/typings/src/utils/validators/parameterName/validateParameterName.d.ts +5 -4
  165. package/esm/typings/src/version.d.ts +2 -1
  166. package/package.json +2 -1
  167. package/umd/index.umd.js +1597 -468
  168. package/umd/index.umd.js.map +1 -1
  169. package/esm/typings/src/formats/_common/FormatSubvalueDefinition.d.ts +0 -31
  170. package/esm/typings/src/formats/csv/CsvFormatDefinition.d.ts +0 -17
  171. 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-30';
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
@@ -3420,7 +3592,7 @@ async function $provideLlmToolsForCli(options) {
3420
3592
  password,
3421
3593
  }),
3422
3594
  });
3423
- const { isSuccess, message, error, identification } = (await response.json());
3595
+ const { isSuccess, message, error, identification } = jsonParse(await response.text());
3424
3596
  if (message) {
3425
3597
  if (isSuccess) {
3426
3598
  console.log(colors.green(message));
@@ -3641,7 +3813,8 @@ function $execCommand(options) {
3641
3813
  */
3642
3814
 
3643
3815
  /**
3644
- * @@@
3816
+ * Attempts to locate the specified application on a Linux system using the 'which' command.
3817
+ * Returns the path to the executable if found, or null otherwise.
3645
3818
  *
3646
3819
  * @private within the repository
3647
3820
  */
@@ -3684,7 +3857,8 @@ async function isExecutable(path, fs) {
3684
3857
  // eslint-disable-next-line @typescript-eslint/no-var-requires
3685
3858
  const userhome = require('userhome');
3686
3859
  /**
3687
- * @@@
3860
+ * Attempts to locate the specified application on a macOS system by checking standard application paths and using mdfind.
3861
+ * Returns the path to the executable if found, or null otherwise.
3688
3862
  *
3689
3863
  * @private within the repository
3690
3864
  */
@@ -3716,7 +3890,8 @@ async function locateAppOnMacOs({ macOsName, }) {
3716
3890
  */
3717
3891
 
3718
3892
  /**
3719
- * @@@
3893
+ * Attempts to locate the specified application on a Windows system by searching common installation directories.
3894
+ * Returns the path to the executable if found, or null otherwise.
3720
3895
  *
3721
3896
  * @private within the repository
3722
3897
  */
@@ -3787,7 +3962,8 @@ function locateApp(options) {
3787
3962
  */
3788
3963
 
3789
3964
  /**
3790
- * @@@
3965
+ * Locates the LibreOffice executable on the current system by searching platform-specific paths.
3966
+ * Returns the path to the executable if found, or null otherwise.
3791
3967
  *
3792
3968
  * @private within the repository
3793
3969
  */
@@ -3805,7 +3981,8 @@ function locateLibreoffice() {
3805
3981
  */
3806
3982
 
3807
3983
  /**
3808
- * @@@
3984
+ * Locates the Pandoc executable on the current system by searching platform-specific paths.
3985
+ * Returns the path to the executable if found, or null otherwise.
3809
3986
  *
3810
3987
  * @private within the repository
3811
3988
  */
@@ -3823,7 +4000,7 @@ function locatePandoc() {
3823
4000
  */
3824
4001
 
3825
4002
  /**
3826
- * @@@
4003
+ * Provides paths to required executables (i.e. as Pandoc and LibreOffice) for Node.js environments.
3827
4004
  *
3828
4005
  * @public exported from `@promptbook/node`
3829
4006
  */
@@ -3843,10 +4020,11 @@ async function $provideExecutablesForNode(options) {
3843
4020
  */
3844
4021
 
3845
4022
  /**
3846
- * @@@
4023
+ * Registry for all available scrapers in the system.
4024
+ * Central point for registering and accessing different types of content scrapers.
3847
4025
  *
3848
4026
  * 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 @@@
4027
+ * @singleton Only one instance of each register is created per build, but there can be more than one in different build modules
3850
4028
  * @public exported from `@promptbook/core`
3851
4029
  */
3852
4030
  const $scrapersRegister = new $Register('scraper_constructors');
@@ -3855,11 +4033,9 @@ const $scrapersRegister = new $Register('scraper_constructors');
3855
4033
  */
3856
4034
 
3857
4035
  /**
3858
- * @@@
3859
- *
3860
- * 1) @@@
3861
- * 2) @@@
3862
- *
4036
+ * Provides a collection of scrapers optimized for Node.js environment.
4037
+ * 1) `provideScrapersForNode` use as default
4038
+ * 2) `provideScrapersForBrowser` use in limited browser environment *
3863
4039
  * @public exported from `@promptbook/node`
3864
4040
  */
3865
4041
  async function $provideScrapersForNode(tools, options) {
@@ -3884,10 +4060,10 @@ async function $provideScrapersForNode(tools, options) {
3884
4060
  */
3885
4061
 
3886
4062
  /**
3887
- * @@@
4063
+ * Global registry for storing metadata about all available scrapers and converters.
3888
4064
  *
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 @@@
4065
+ * Note: `$` is used to indicate that this interacts with the global scope.
4066
+ * @singleton Only one instance of each register is created per build, but there can be more in different contexts (e.g., tests).
3891
4067
  * @public exported from `@promptbook/core`
3892
4068
  */
3893
4069
  const $scrapersMetadataRegister = new $Register('scrapers_metadata');
@@ -4451,7 +4627,7 @@ async function loadArchive(filePath, fs) {
4451
4627
  if (!indexFile) {
4452
4628
  throw new UnexpectedError(`Archive does not contain 'index.book.json' file`);
4453
4629
  }
4454
- const collectionJson = JSON.parse(await indexFile.async('text'));
4630
+ const collectionJson = jsonParse(await indexFile.async('text'));
4455
4631
  for (const pipeline of collectionJson) {
4456
4632
  validatePipeline(pipeline);
4457
4633
  }
@@ -4461,13 +4637,13 @@ async function loadArchive(filePath, fs) {
4461
4637
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
4462
4638
  */
4463
4639
 
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"}];
4640
+ 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
4641
 
4466
4642
  /**
4467
4643
  * Function isValidJsonString will tell you if the string is valid JSON or not
4468
4644
  *
4469
4645
  * @param value The string to check
4470
- * @returns True if the string is a valid JSON string, false otherwise
4646
+ * @returns `true` if the string is a valid JSON string, false otherwise
4471
4647
  *
4472
4648
  * @public exported from `@promptbook/utils`
4473
4649
  */
@@ -4697,23 +4873,6 @@ function taskParameterJsonToString(taskParameterJson) {
4697
4873
  * TODO: [🧠] Should be in generated .book.md file GENERATOR_WARNING
4698
4874
  */
4699
4875
 
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
4876
  /**
4718
4877
  * Unprepare just strips the preparation data of the pipeline
4719
4878
  *
@@ -4722,7 +4881,7 @@ function extractParameterNames(template) {
4722
4881
  */
4723
4882
  function unpreparePipeline(pipeline) {
4724
4883
  let { personas, knowledgeSources, tasks } = pipeline;
4725
- personas = personas.map((persona) => ({ ...persona, modelRequirements: undefined, preparationIds: undefined }));
4884
+ personas = personas.map((persona) => ({ ...persona, modelsRequirements: undefined, preparationIds: undefined }));
4726
4885
  knowledgeSources = knowledgeSources.map((knowledgeSource) => ({ ...knowledgeSource, preparationIds: undefined }));
4727
4886
  tasks = tasks.map((task) => {
4728
4887
  let { dependentParameterNames } = task;
@@ -4763,7 +4922,7 @@ class SimplePipelineCollection {
4763
4922
  /**
4764
4923
  * Constructs a pipeline collection from pipelines
4765
4924
  *
4766
- * @param pipelines @@@
4925
+ * @param pipelines Array of pipeline JSON objects to include in the collection
4767
4926
  *
4768
4927
  * Note: During the construction logic of all pipelines are validated
4769
4928
  * Note: It is not recommended to use this constructor directly, use `createCollectionFromJson` *(or other variant)* instead
@@ -4875,15 +5034,21 @@ function createCollectionFromJson(...promptbooks) {
4875
5034
  * @public exported from `@promptbook/core`
4876
5035
  */
4877
5036
  function isPipelinePrepared(pipeline) {
4878
- // Note: Ignoring `pipeline.preparations` @@@
4879
- // Note: Ignoring `pipeline.knowledgePieces` @@@
5037
+ // Note: Ignoring `pipeline.preparations`
5038
+ // Note: Ignoring `pipeline.knowledgePieces`
4880
5039
  if (pipeline.title === undefined || pipeline.title === '' || pipeline.title === DEFAULT_BOOK_TITLE) {
5040
+ // TODO: !!! Comment this out
5041
+ console.log('Pipeline is not prepared because title is undefined or empty', pipeline);
4881
5042
  return false;
4882
5043
  }
4883
- if (!pipeline.personas.every((persona) => persona.modelRequirements !== undefined)) {
5044
+ if (!pipeline.personas.every((persona) => persona.modelsRequirements !== undefined)) {
5045
+ // TODO: !!! Comment this out
5046
+ console.log('Pipeline is not prepared because personas are not prepared', pipeline.personas);
4884
5047
  return false;
4885
5048
  }
4886
5049
  if (!pipeline.knowledgeSources.every((knowledgeSource) => knowledgeSource.preparationIds !== undefined)) {
5050
+ // TODO: !!! Comment this out
5051
+ console.log('Pipeline is not prepared because knowledge sources are not prepared', pipeline.knowledgeSources);
4887
5052
  return false;
4888
5053
  }
4889
5054
  /*
@@ -4904,6 +5069,35 @@ function isPipelinePrepared(pipeline) {
4904
5069
  * - [♨] Are tasks prepared
4905
5070
  */
4906
5071
 
5072
+ /**
5073
+ * Serializes an error into a [🚉] JSON-serializable object
5074
+ *
5075
+ * @public exported from `@promptbook/utils`
5076
+ */
5077
+ function serializeError(error) {
5078
+ const { name, message, stack } = error;
5079
+ const { id } = error;
5080
+ if (!Object.keys(ALL_ERRORS).includes(name)) {
5081
+ console.error(spaceTrim((block) => `
5082
+
5083
+ Cannot serialize error with name "${name}"
5084
+
5085
+ Authors of Promptbook probably forgot to add this error into the list of errors:
5086
+ https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
5087
+
5088
+
5089
+ ${block(stack || message)}
5090
+
5091
+ `));
5092
+ }
5093
+ return {
5094
+ name: name,
5095
+ message,
5096
+ stack,
5097
+ id, // Include id in the serialized object
5098
+ };
5099
+ }
5100
+
4907
5101
  /**
4908
5102
  * Recursively converts JSON strings to JSON objects
4909
5103
 
@@ -4922,7 +5116,7 @@ function jsonStringsToJsons(object) {
4922
5116
  const newObject = { ...object };
4923
5117
  for (const [key, value] of Object.entries(object)) {
4924
5118
  if (typeof value === 'string' && isValidJsonString(value)) {
4925
- newObject[key] = JSON.parse(value);
5119
+ newObject[key] = jsonParse(value);
4926
5120
  }
4927
5121
  else {
4928
5122
  newObject[key] = jsonStringsToJsons(value);
@@ -5073,35 +5267,6 @@ function createTask(options) {
5073
5267
  * TODO: [🐚] Split into more files and make `PrepareTask` & `RemoteTask` + split the function
5074
5268
  */
5075
5269
 
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
5270
  /**
5106
5271
  * Format either small or big number
5107
5272
  *
@@ -5344,7 +5509,7 @@ function union(...sets) {
5344
5509
  }
5345
5510
 
5346
5511
  /**
5347
- * @@@
5512
+ * Contains configuration options for parsing and generating CSV files, such as delimiters and quoting rules.
5348
5513
  *
5349
5514
  * @public exported from `@promptbook/core`
5350
5515
  */
@@ -5353,11 +5518,29 @@ const MANDATORY_CSV_SETTINGS = Object.freeze({
5353
5518
  // encoding: 'utf-8',
5354
5519
  });
5355
5520
 
5521
+ /**
5522
+ * Converts a CSV string into an object
5523
+ *
5524
+ * Note: This is wrapper around `papaparse.parse()` with better autohealing
5525
+ *
5526
+ * @private - for now until `@promptbook/csv` is released
5527
+ */
5528
+ function csvParse(value /* <- TODO: string_csv */, settings, schema /* <- TODO: Make CSV Schemas */) {
5529
+ settings = { ...settings, ...MANDATORY_CSV_SETTINGS };
5530
+ // Note: Autoheal invalid '\n' characters
5531
+ if (settings.newline && !settings.newline.includes('\r') && value.includes('\r')) {
5532
+ console.warn('CSV string contains carriage return characters, but in the CSV settings the `newline` setting does not include them. Autohealing the CSV string.');
5533
+ value = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
5534
+ }
5535
+ const csv = parse(value, settings);
5536
+ return csv;
5537
+ }
5538
+
5356
5539
  /**
5357
5540
  * Function to check if a string is valid CSV
5358
5541
  *
5359
5542
  * @param value The string to check
5360
- * @returns True if the string is a valid CSV string, false otherwise
5543
+ * @returns `true` if the string is a valid CSV string, false otherwise
5361
5544
  *
5362
5545
  * @public exported from `@promptbook/utils`
5363
5546
  */
@@ -5381,7 +5564,7 @@ function isValidCsvString(value) {
5381
5564
  * @public exported from `@promptbook/core`
5382
5565
  * <- TODO: [🏢] Export from package `@promptbook/csv`
5383
5566
  */
5384
- const CsvFormatDefinition = {
5567
+ const CsvFormatParser = {
5385
5568
  formatName: 'CSV',
5386
5569
  aliases: ['SPREADSHEET', 'TABLE'],
5387
5570
  isValid(value, settings, schema) {
@@ -5393,12 +5576,12 @@ const CsvFormatDefinition = {
5393
5576
  heal(value, settings, schema) {
5394
5577
  throw new Error('Not implemented');
5395
5578
  },
5396
- subvalueDefinitions: [
5579
+ subvalueParsers: [
5397
5580
  {
5398
5581
  subvalueName: 'ROW',
5399
- async mapValues(value, outputParameterName, settings, mapCallback) {
5400
- // TODO: [👨🏾‍🤝‍👨🏼] DRY csv parsing
5401
- const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
5582
+ async mapValues(options) {
5583
+ const { value, outputParameterName, settings, mapCallback, onProgress } = options;
5584
+ const csv = csvParse(value, settings);
5402
5585
  if (csv.errors.length !== 0) {
5403
5586
  throw new CsvFormatError(spaceTrim((block) => `
5404
5587
  CSV parsing error
@@ -5413,23 +5596,37 @@ const CsvFormatDefinition = {
5413
5596
  ${block(value)}
5414
5597
  `));
5415
5598
  }
5416
- const mappedData = await Promise.all(csv.data.map(async (row, index) => {
5599
+ const mappedData = [];
5600
+ const length = csv.data.length;
5601
+ for (let index = 0; index < length; index++) {
5602
+ const row = csv.data[index];
5417
5603
  if (row[outputParameterName]) {
5418
5604
  throw new CsvFormatError(`Can not overwrite existing column "${outputParameterName}" in CSV row`);
5419
5605
  }
5420
- return {
5606
+ const mappedRow = {
5421
5607
  ...row,
5422
- [outputParameterName]: await mapCallback(row, index),
5608
+ [outputParameterName]: await mapCallback(row, index, length),
5423
5609
  };
5424
- }));
5610
+ mappedData.push(mappedRow);
5611
+ if (onProgress) {
5612
+ // Note: Report the CSV with all rows mapped so far
5613
+ /*
5614
+ // TODO: [🛕] Report progress with all the rows including the pending ones
5615
+ const progressData = mappedData.map((row, i) =>
5616
+ i > index ? { ...row, [outputParameterName]: PENDING_VALUE_PLACEHOLDER } : row,
5617
+ );
5618
+ */
5619
+ await onProgress(unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS }));
5620
+ }
5621
+ }
5425
5622
  return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
5426
5623
  },
5427
5624
  },
5428
5625
  {
5429
5626
  subvalueName: 'CELL',
5430
- async mapValues(value, outputParameterName, settings, mapCallback) {
5431
- // TODO: [👨🏾‍🤝‍👨🏼] DRY csv parsing
5432
- const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
5627
+ async mapValues(options) {
5628
+ const { value, settings, mapCallback, onProgress } = options;
5629
+ const csv = csvParse(value, settings);
5433
5630
  if (csv.errors.length !== 0) {
5434
5631
  throw new CsvFormatError(spaceTrim((block) => `
5435
5632
  CSV parsing error
@@ -5445,9 +5642,9 @@ const CsvFormatDefinition = {
5445
5642
  `));
5446
5643
  }
5447
5644
  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) => {
5645
+ return /* not await */ Promise.all(Object.entries(row).map(async ([key, value], columnIndex, array) => {
5449
5646
  const index = rowIndex * Object.keys(row).length + columnIndex;
5450
- return /* not await */ mapCallback({ [key]: value }, index);
5647
+ return /* not await */ mapCallback({ [key]: value }, index, array.length);
5451
5648
  }));
5452
5649
  }));
5453
5650
  return unparse(mappedData, { ...settings, ...MANDATORY_CSV_SETTINGS });
@@ -5456,10 +5653,10 @@ const CsvFormatDefinition = {
5456
5653
  ],
5457
5654
  };
5458
5655
  /**
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`
5656
+ * TODO: [🍓] In `CsvFormatParser` implement simple `isValid`
5657
+ * TODO: [🍓] In `CsvFormatParser` implement partial `canBeValid`
5658
+ * TODO: [🍓] In `CsvFormatParser` implement `heal
5659
+ * TODO: [🍓] In `CsvFormatParser` implement `subvalueParsers`
5463
5660
  * TODO: [🏢] Allow to expect something inside CSV objects and other formats
5464
5661
  */
5465
5662
 
@@ -5468,7 +5665,7 @@ const CsvFormatDefinition = {
5468
5665
  *
5469
5666
  * @private still in development [🏢]
5470
5667
  */
5471
- const JsonFormatDefinition = {
5668
+ const JsonFormatParser = {
5472
5669
  formatName: 'JSON',
5473
5670
  mimeType: 'application/json',
5474
5671
  isValid(value, settings, schema) {
@@ -5480,28 +5677,28 @@ const JsonFormatDefinition = {
5480
5677
  heal(value, settings, schema) {
5481
5678
  throw new Error('Not implemented');
5482
5679
  },
5483
- subvalueDefinitions: [],
5680
+ subvalueParsers: [],
5484
5681
  };
5485
5682
  /**
5486
5683
  * TODO: [🧠] Maybe propper instance of object
5487
5684
  * TODO: [0] Make string_serialized_json
5488
5685
  * TODO: [1] Make type for JSON Settings and Schema
5489
5686
  * 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`
5687
+ * TODO: [🍓] In `JsonFormatParser` implement simple `isValid`
5688
+ * TODO: [🍓] In `JsonFormatParser` implement partial `canBeValid`
5689
+ * TODO: [🍓] In `JsonFormatParser` implement `heal
5690
+ * TODO: [🍓] In `JsonFormatParser` implement `subvalueParsers`
5494
5691
  * TODO: [🏢] Allow to expect something inside JSON objects and other formats
5495
5692
  */
5496
5693
 
5497
5694
  /**
5498
5695
  * Definition for any text - this will be always valid
5499
5696
  *
5500
- * Note: This is not useful for validation, but for splitting and mapping with `subvalueDefinitions`
5697
+ * Note: This is not useful for validation, but for splitting and mapping with `subvalueParsers`
5501
5698
  *
5502
5699
  * @public exported from `@promptbook/core`
5503
5700
  */
5504
- const TextFormatDefinition = {
5701
+ const TextFormatParser = {
5505
5702
  formatName: 'TEXT',
5506
5703
  isValid(value) {
5507
5704
  return typeof value === 'string';
@@ -5510,19 +5707,20 @@ const TextFormatDefinition = {
5510
5707
  return typeof partialValue === 'string';
5511
5708
  },
5512
5709
  heal() {
5513
- throw new UnexpectedError('It does not make sense to call `TextFormatDefinition.heal`');
5710
+ throw new UnexpectedError('It does not make sense to call `TextFormatParser.heal`');
5514
5711
  },
5515
- subvalueDefinitions: [
5712
+ subvalueParsers: [
5516
5713
  {
5517
5714
  subvalueName: 'LINE',
5518
- async mapValues(value, outputParameterName, settings, mapCallback) {
5715
+ async mapValues(options) {
5716
+ const { value, mapCallback, onProgress } = options;
5519
5717
  const lines = value.split('\n');
5520
- const mappedLines = await Promise.all(lines.map((lineContent, lineNumber) =>
5718
+ const mappedLines = await Promise.all(lines.map((lineContent, lineNumber, array) =>
5521
5719
  // TODO: [🧠] Maybe option to skip empty line
5522
5720
  /* not await */ mapCallback({
5523
5721
  lineContent,
5524
5722
  // TODO: [🧠] Maybe also put here `lineNumber`
5525
- }, lineNumber)));
5723
+ }, lineNumber, array.length)));
5526
5724
  return mappedLines.join('\n');
5527
5725
  },
5528
5726
  },
@@ -5532,10 +5730,10 @@ const TextFormatDefinition = {
5532
5730
  /**
5533
5731
  * TODO: [1] Make type for XML Text and Schema
5534
5732
  * 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`
5733
+ * TODO: [🍓] In `TextFormatParser` implement simple `isValid`
5734
+ * TODO: [🍓] In `TextFormatParser` implement partial `canBeValid`
5735
+ * TODO: [🍓] In `TextFormatParser` implement `heal
5736
+ * TODO: [🍓] In `TextFormatParser` implement `subvalueParsers`
5539
5737
  * TODO: [🏢] Allow to expect something inside each item of list and other formats
5540
5738
  */
5541
5739
 
@@ -5543,7 +5741,7 @@ const TextFormatDefinition = {
5543
5741
  * Function to check if a string is valid XML
5544
5742
  *
5545
5743
  * @param value
5546
- * @returns True if the string is a valid XML string, false otherwise
5744
+ * @returns `true` if the string is a valid XML string, false otherwise
5547
5745
  *
5548
5746
  * @public exported from `@promptbook/utils`
5549
5747
  */
@@ -5568,7 +5766,7 @@ function isValidXmlString(value) {
5568
5766
  *
5569
5767
  * @private still in development [🏢]
5570
5768
  */
5571
- const XmlFormatDefinition = {
5769
+ const XmlFormatParser = {
5572
5770
  formatName: 'XML',
5573
5771
  mimeType: 'application/xml',
5574
5772
  isValid(value, settings, schema) {
@@ -5580,17 +5778,17 @@ const XmlFormatDefinition = {
5580
5778
  heal(value, settings, schema) {
5581
5779
  throw new Error('Not implemented');
5582
5780
  },
5583
- subvalueDefinitions: [],
5781
+ subvalueParsers: [],
5584
5782
  };
5585
5783
  /**
5586
5784
  * TODO: [🧠] Maybe propper instance of object
5587
5785
  * TODO: [0] Make string_serialized_xml
5588
5786
  * TODO: [1] Make type for XML Settings and Schema
5589
5787
  * 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`
5788
+ * TODO: [🍓] In `XmlFormatParser` implement simple `isValid`
5789
+ * TODO: [🍓] In `XmlFormatParser` implement partial `canBeValid`
5790
+ * TODO: [🍓] In `XmlFormatParser` implement `heal
5791
+ * TODO: [🍓] In `XmlFormatParser` implement `subvalueParsers`
5594
5792
  * TODO: [🏢] Allow to expect something inside XML and other formats
5595
5793
  */
5596
5794
 
@@ -5599,24 +5797,19 @@ const XmlFormatDefinition = {
5599
5797
  *
5600
5798
  * @private internal index of `...` <- TODO [🏢]
5601
5799
  */
5602
- const FORMAT_DEFINITIONS = [
5603
- JsonFormatDefinition,
5604
- XmlFormatDefinition,
5605
- TextFormatDefinition,
5606
- CsvFormatDefinition,
5607
- ];
5800
+ const FORMAT_DEFINITIONS = [JsonFormatParser, XmlFormatParser, TextFormatParser, CsvFormatParser];
5608
5801
  /**
5609
5802
  * Note: [💞] Ignore a discrepancy between file name and entity name
5610
5803
  */
5611
5804
 
5612
5805
  /**
5613
- * Maps available parameters to expected parameters
5806
+ * Maps available parameters to expected parameters for a pipeline task.
5614
5807
  *
5615
5808
  * The strategy is:
5616
- * 1) @@@
5617
- * 2) @@@
5809
+ * 1) First, match parameters by name where both available and expected.
5810
+ * 2) Then, if there are unmatched expected and available parameters, map them by order.
5618
5811
  *
5619
- * @throws {PipelineExecutionError} @@@
5812
+ * @throws {PipelineExecutionError} If the number of unmatched expected and available parameters does not match, or mapping is ambiguous.
5620
5813
  * @private within the repository used in `createPipelineExecutor`
5621
5814
  */
5622
5815
  function mapAvailableToExpectedParameters(options) {
@@ -5639,7 +5832,7 @@ function mapAvailableToExpectedParameters(options) {
5639
5832
  else if (!availableParametersNames.has(parameterName) && expectedParameterNames.has(parameterName)) ;
5640
5833
  }
5641
5834
  if (expectedParameterNames.size === 0) {
5642
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
5835
+ // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent accidental modifications after mapping
5643
5836
  Object.freeze(mappedParameters);
5644
5837
  return mappedParameters;
5645
5838
  }
@@ -5670,7 +5863,7 @@ function mapAvailableToExpectedParameters(options) {
5670
5863
  for (let i = 0; i < expectedParameterNames.size; i++) {
5671
5864
  mappedParameters[expectedParameterNamesArray[i]] = availableParameters[availableParametersNamesArray[i]];
5672
5865
  }
5673
- // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent @@@
5866
+ // Note: [👨‍👨‍👧] Now we can freeze `mappedParameters` to prevent accidental modifications after mapping
5674
5867
  Object.freeze(mappedParameters);
5675
5868
  return mappedParameters;
5676
5869
  }
@@ -5774,7 +5967,7 @@ function extractJsonBlock(markdown) {
5774
5967
  }
5775
5968
  /**
5776
5969
  * TODO: Add some auto-healing logic + extract YAML, JSON5, TOML, etc.
5777
- * TODO: [🏢] Make this logic part of `JsonFormatDefinition` or `isValidJsonString`
5970
+ * TODO: [🏢] Make this logic part of `JsonFormatParser` or `isValidJsonString`
5778
5971
  */
5779
5972
 
5780
5973
  /**
@@ -5836,10 +6029,12 @@ function templateParameters(template, parameters) {
5836
6029
  throw new PipelineExecutionError('Parameter is already opened or not closed');
5837
6030
  }
5838
6031
  if (parameters[parameterName] === undefined) {
6032
+ console.log('!!! templateParameters 1', { parameterName, template, parameters });
5839
6033
  throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
5840
6034
  }
5841
6035
  let parameterValue = parameters[parameterName];
5842
6036
  if (parameterValue === undefined) {
6037
+ console.log('!!! templateParameters 2', { parameterName, template, parameters });
5843
6038
  throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
5844
6039
  }
5845
6040
  parameterValue = valueToString(parameterValue);
@@ -5995,7 +6190,7 @@ const CountUtils = {
5995
6190
  PAGES: countPages,
5996
6191
  };
5997
6192
  /**
5998
- * TODO: [🧠][🤠] This should be probbably as part of `TextFormatDefinition`
6193
+ * TODO: [🧠][🤠] This should be probbably as part of `TextFormatParser`
5999
6194
  * Note: [💞] Ignore a discrepancy between file name and entity name
6000
6195
  */
6001
6196
 
@@ -6023,13 +6218,17 @@ function checkExpectations(expectations, value) {
6023
6218
  }
6024
6219
  /**
6025
6220
  * TODO: [💝] Unite object for expecting amount and format
6026
- * TODO: [🧠][🤠] This should be part of `TextFormatDefinition`
6221
+ * TODO: [🧠][🤠] This should be part of `TextFormatParser`
6027
6222
  * Note: [💝] and [🤠] are interconnected together
6028
6223
  */
6029
6224
 
6030
6225
  /**
6031
- * @@@
6226
+ * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
6227
+ * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
6228
+ * Throws errors if execution fails after all attempts.
6032
6229
  *
6230
+ * @param options - The options for execution, including task, parameters, pipeline, and configuration.
6231
+ * @returns The result string of the executed task.
6033
6232
  * @private internal utility of `createPipelineExecutor`
6034
6233
  */
6035
6234
  async function executeAttempts(options) {
@@ -6251,7 +6450,7 @@ async function executeAttempts(options) {
6251
6450
  if (task.format) {
6252
6451
  if (task.format === 'JSON') {
6253
6452
  if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
6254
- // TODO: [🏢] Do more universally via `FormatDefinition`
6453
+ // TODO: [🏢] Do more universally via `FormatParser`
6255
6454
  try {
6256
6455
  $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
6257
6456
  }
@@ -6353,12 +6552,16 @@ async function executeAttempts(options) {
6353
6552
  */
6354
6553
 
6355
6554
  /**
6356
- * @@@
6555
+ * Executes a pipeline task that requires mapping or iterating over subvalues of a parameter (such as rows in a CSV).
6556
+ * Handles format and subformat resolution, error handling, and progress reporting.
6557
+ *
6558
+ * @param options - Options for execution, including task details and progress callback.
6559
+ * @returns The result of the subvalue mapping or execution attempts.
6357
6560
  *
6358
6561
  * @private internal utility of `createPipelineExecutor`
6359
6562
  */
6360
6563
  async function executeFormatSubvalues(options) {
6361
- const { task, jokerParameterNames, parameters, priority, csvSettings, pipelineIdentification } = options;
6564
+ const { task, jokerParameterNames, parameters, priority, csvSettings, onProgress, pipelineIdentification } = options;
6362
6565
  if (task.foreach === undefined) {
6363
6566
  return /* not await */ executeAttempts(options);
6364
6567
  }
@@ -6389,16 +6592,16 @@ async function executeFormatSubvalues(options) {
6389
6592
  ${block(pipelineIdentification)}
6390
6593
  `));
6391
6594
  }
6392
- const subvalueDefinition = formatDefinition.subvalueDefinitions.find((subvalueDefinition) => [subvalueDefinition.subvalueName, ...(subvalueDefinition.aliases || [])].includes(task.foreach.subformatName));
6393
- if (subvalueDefinition === undefined) {
6595
+ const subvalueParser = formatDefinition.subvalueParsers.find((subvalueParser) => [subvalueParser.subvalueName, ...(subvalueParser.aliases || [])].includes(task.foreach.subformatName));
6596
+ if (subvalueParser === undefined) {
6394
6597
  throw new UnexpectedError(
6395
6598
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
6396
6599
  spaceTrim((block) => `
6397
6600
  Unsupported subformat name "${task.foreach.subformatName}" for format "${task.foreach.formatName}"
6398
6601
 
6399
6602
  Available subformat names for format "${formatDefinition.formatName}":
6400
- ${block(formatDefinition.subvalueDefinitions
6401
- .map((subvalueDefinition) => subvalueDefinition.subvalueName)
6603
+ ${block(formatDefinition.subvalueParsers
6604
+ .map((subvalueParser) => subvalueParser.subvalueName)
6402
6605
  .map((subvalueName) => `- ${subvalueName}`)
6403
6606
  .join('\n'))}
6404
6607
 
@@ -6412,53 +6615,83 @@ async function executeFormatSubvalues(options) {
6412
6615
  formatSettings = csvSettings;
6413
6616
  // <- TODO: [🤹‍♂️] More universal, make simmilar pattern for other formats for example \n vs \r\n in text
6414
6617
  }
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;
6618
+ const resultString = await subvalueParser.mapValues({
6619
+ value: parameterValue,
6620
+ outputParameterName: task.foreach.outputSubparameterName,
6621
+ settings: formatSettings,
6622
+ onProgress(partialResultString) {
6623
+ return onProgress(Object.freeze({
6624
+ [task.resultingParameterName]: partialResultString,
6625
+ }));
6626
+ },
6627
+ async mapCallback(subparameters, index, length) {
6628
+ let mappedParameters;
6629
+ try {
6630
+ mappedParameters = mapAvailableToExpectedParameters({
6631
+ expectedParameters: Object.fromEntries(task.foreach.inputSubparameterNames.map((subparameterName) => [subparameterName, null])),
6632
+ availableParameters: subparameters,
6633
+ });
6428
6634
  }
6429
- throw new PipelineExecutionError(spaceTrim((block) => `
6430
- ${error.message}
6635
+ catch (error) {
6636
+ if (!(error instanceof PipelineExecutionError)) {
6637
+ throw error;
6638
+ }
6639
+ const highLevelError = new PipelineExecutionError(spaceTrim((block) => `
6640
+ ${error.message}
6431
6641
 
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
6642
+ This is error in FOREACH command when mapping ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
6643
+ You have probbably passed wrong data to pipeline or wrong data was generated which are processed by FOREACH command
6434
6644
 
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;
6645
+ ${block(pipelineIdentification)}
6646
+ `));
6647
+ if (length > BIG_DATASET_TRESHOLD) {
6648
+ console.error(highLevelError);
6649
+ return FAILED_VALUE_PLACEHOLDER;
6650
+ }
6651
+ throw highLevelError;
6652
+ }
6653
+ const allSubparameters = {
6654
+ ...parameters,
6655
+ ...mappedParameters,
6656
+ };
6657
+ Object.freeze(allSubparameters);
6658
+ try {
6659
+ const subresultString = await executeAttempts({
6660
+ ...options,
6661
+ priority: priority + index,
6662
+ parameters: allSubparameters,
6663
+ pipelineIdentification: spaceTrim((block) => `
6664
+ ${block(pipelineIdentification)}
6665
+ Subparameter index: ${index}
6666
+ `),
6667
+ });
6668
+ return subresultString;
6669
+ }
6670
+ catch (error) {
6671
+ if (length > BIG_DATASET_TRESHOLD) {
6672
+ console.error(spaceTrim((block) => `
6673
+ ${error.message}
6674
+
6675
+ This is error in FOREACH command when processing ${formatDefinition.formatName} ${subvalueParser.subvalueName} data (${index + 1}/${length})
6676
+
6677
+ ${block(pipelineIdentification)}
6678
+ `));
6679
+ return FAILED_VALUE_PLACEHOLDER;
6680
+ }
6681
+ throw error;
6682
+ }
6683
+ },
6455
6684
  });
6456
6685
  return resultString;
6457
6686
  }
6458
6687
 
6459
6688
  /**
6460
- * @@@
6689
+ * Returns the context for a given task, typically used to provide additional information or variables
6690
+ * required for the execution of the task within a pipeline. The context is returned as a string value
6691
+ * that may include markdown formatting.
6461
6692
  *
6693
+ * @param task - The task for which the context is being generated. This should be a deeply immutable TaskJson object.
6694
+ * @returns The context as a string, formatted as markdown and parameter value.
6462
6695
  * @private internal utility of `createPipelineExecutor`
6463
6696
  */
6464
6697
  async function getContextForTask(task) {
@@ -6466,7 +6699,7 @@ async function getContextForTask(task) {
6466
6699
  }
6467
6700
 
6468
6701
  /**
6469
- * @@@
6702
+ * Retrieves example values or templates for a given task, used to guide or validate pipeline execution.
6470
6703
  *
6471
6704
  * @private internal utility of `createPipelineExecutor`
6472
6705
  */
@@ -6475,25 +6708,127 @@ async function getExamplesForTask(task) {
6475
6708
  }
6476
6709
 
6477
6710
  /**
6478
- * @@@
6711
+ * Computes the cosine similarity between two embedding vectors
6712
+ *
6713
+ * Note: This is helping function for RAG (retrieval-augmented generation)
6714
+ *
6715
+ * @param embeddingVector1
6716
+ * @param embeddingVector2
6717
+ * @returns Cosine similarity between the two vectors
6718
+ *
6719
+ * @public exported from `@promptbook/core`
6720
+ */
6721
+ function computeCosineSimilarity(embeddingVector1, embeddingVector2) {
6722
+ if (embeddingVector1.length !== embeddingVector2.length) {
6723
+ throw new TypeError('Embedding vectors must have the same length');
6724
+ }
6725
+ const dotProduct = embeddingVector1.reduce((sum, value, index) => sum + value * embeddingVector2[index], 0);
6726
+ const magnitude1 = Math.sqrt(embeddingVector1.reduce((sum, value) => sum + value * value, 0));
6727
+ const magnitude2 = Math.sqrt(embeddingVector2.reduce((sum, value) => sum + value * value, 0));
6728
+ return 1 - dotProduct / (magnitude1 * magnitude2);
6729
+ }
6730
+
6731
+ /**
6732
+ *
6733
+ * @param knowledgePieces
6734
+ * @returns
6735
+ *
6736
+ * @private internal utility of `createPipelineExecutor`
6737
+ */
6738
+ function knowledgePiecesToString(knowledgePieces) {
6739
+ return knowledgePieces
6740
+ .map((knowledgePiece) => {
6741
+ const { content } = knowledgePiece;
6742
+ return `- ${content}`;
6743
+ })
6744
+ .join('\n');
6745
+ // <- TODO: [🧠] Some smarter aggregation of knowledge pieces, single-line vs multi-line vs mixed
6746
+ }
6747
+
6748
+ /**
6749
+ * Retrieves the most relevant knowledge pieces for a given task using embedding-based similarity search.
6750
+ * This is where retrieval-augmented generation (RAG) is performed to enhance the task with external knowledge.
6479
6751
  *
6480
6752
  * @private internal utility of `createPipelineExecutor`
6481
6753
  */
6482
6754
  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
6755
+ const { tools, preparedPipeline, task, parameters } = options;
6756
+ const firstKnowlegePiece = preparedPipeline.knowledgePieces[0];
6757
+ const firstKnowlegeIndex = firstKnowlegePiece === null || firstKnowlegePiece === void 0 ? void 0 : firstKnowlegePiece.index[0];
6758
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model, use also keyword search
6759
+ if (firstKnowlegePiece === undefined || firstKnowlegeIndex === undefined) {
6760
+ return ''; // <- Note: Np knowledge present, return empty string
6761
+ }
6762
+ try {
6763
+ // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
6764
+ const _llms = arrayableToArray(tools.llm);
6765
+ const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
6766
+ const taskEmbeddingPrompt = {
6767
+ title: 'Knowledge Search',
6768
+ modelRequirements: {
6769
+ modelVariant: 'EMBEDDING',
6770
+ modelName: firstKnowlegeIndex.modelName,
6771
+ },
6772
+ content: task.content,
6773
+ parameters,
6774
+ };
6775
+ const taskEmbeddingResult = await llmTools.callEmbeddingModel(taskEmbeddingPrompt);
6776
+ const knowledgePiecesWithRelevance = preparedPipeline.knowledgePieces.map((knowledgePiece) => {
6777
+ const { index } = knowledgePiece;
6778
+ const knowledgePieceIndex = index.find((i) => i.modelName === firstKnowlegeIndex.modelName);
6779
+ // <- TODO: Do not use just first knowledge piece and first index to determine embedding model
6780
+ if (knowledgePieceIndex === undefined) {
6781
+ return {
6782
+ content: knowledgePiece.content,
6783
+ relevance: 0,
6784
+ };
6785
+ }
6786
+ const relevance = computeCosineSimilarity(knowledgePieceIndex.position, taskEmbeddingResult.content);
6787
+ return {
6788
+ content: knowledgePiece.content,
6789
+ relevance,
6790
+ };
6791
+ });
6792
+ const knowledgePiecesSorted = knowledgePiecesWithRelevance.sort((a, b) => a.relevance - b.relevance);
6793
+ const knowledgePiecesLimited = knowledgePiecesSorted.slice(0, 5);
6794
+ console.log('!!! Embedding', {
6795
+ task,
6796
+ taskEmbeddingPrompt,
6797
+ taskEmbeddingResult,
6798
+ firstKnowlegePiece,
6799
+ firstKnowlegeIndex,
6800
+ knowledgePiecesWithRelevance,
6801
+ knowledgePiecesSorted,
6802
+ knowledgePiecesLimited,
6803
+ });
6804
+ return knowledgePiecesToString(knowledgePiecesLimited);
6805
+ }
6806
+ catch (error) {
6807
+ assertsError(error);
6808
+ console.error('Error in `getKnowledgeForTask`', error);
6809
+ // Note: If the LLM fails, just return all knowledge pieces
6810
+ return knowledgePiecesToString(preparedPipeline.knowledgePieces);
6811
+ }
6486
6812
  }
6813
+ /**
6814
+ * TODO: !!!! Verify if this is working
6815
+ * TODO: [♨] Implement Better - use keyword search
6816
+ * TODO: [♨] Examples of values
6817
+ */
6487
6818
 
6488
6819
  /**
6489
- * @@@
6820
+ * Retrieves all reserved parameters for a given pipeline task, including context, knowledge, examples, and metadata.
6821
+ * Ensures all reserved parameters are defined and throws if any are missing.
6822
+ *
6823
+ * @param options - Options including tools, pipeline, task, and context.
6824
+ * @returns An object containing all reserved parameters for the task.
6490
6825
  *
6491
6826
  * @private internal utility of `createPipelineExecutor`
6492
6827
  */
6493
6828
  async function getReservedParametersForTask(options) {
6494
- const { preparedPipeline, task, pipelineIdentification } = options;
6829
+ const { tools, preparedPipeline, task, parameters, pipelineIdentification } = options;
6495
6830
  const context = await getContextForTask(); // <- [🏍]
6496
- const knowledge = await getKnowledgeForTask({ preparedPipeline, task });
6831
+ const knowledge = await getKnowledgeForTask({ tools, preparedPipeline, task, parameters });
6497
6832
  const examples = await getExamplesForTask();
6498
6833
  const currentDate = new Date().toISOString(); // <- TODO: [🧠][💩] Better
6499
6834
  const modelName = RESERVED_PARAMETER_MISSING_VALUE;
@@ -6519,23 +6854,21 @@ async function getReservedParametersForTask(options) {
6519
6854
  }
6520
6855
 
6521
6856
  /**
6522
- * @@@
6857
+ * Executes a single task within a pipeline, handling parameter validation, error checking, and progress reporting.
6858
+ *
6859
+ * @param options - Options for execution, including the task, pipeline, parameters, and callbacks.
6860
+ * @returns The output parameters produced by the task.
6523
6861
  *
6524
6862
  * @private internal utility of `createPipelineExecutor`
6525
6863
  */
6526
6864
  async function executeTask(options) {
6527
6865
  const { currentTask, preparedPipeline, parametersToPass, tools, onProgress, $executionReport, pipelineIdentification, maxExecutionAttempts, maxParallelCount, csvSettings, isVerbose, rootDirname, cacheDirname, intermediateFilesStrategy, isAutoInstalled, isNotPreparedWarningSupressed, } = options;
6528
6866
  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
- });
6534
6867
  // Note: Check consistency of used and dependent parameters which was also done in `validatePipeline`, but it’s good to doublecheck
6535
6868
  const usedParameterNames = extractParameterNamesFromTask(currentTask);
6536
6869
  const dependentParameterNames = new Set(currentTask.dependentParameterNames);
6537
6870
  // TODO: [👩🏾‍🤝‍👩🏻] Use here `mapAvailableToExpectedParameters`
6538
- if (union(difference(usedParameterNames, dependentParameterNames), difference(dependentParameterNames, usedParameterNames)).size !== 0) {
6871
+ if (difference(union(difference(usedParameterNames, dependentParameterNames), difference(dependentParameterNames, usedParameterNames)), new Set(RESERVED_PARAMETER_NAMES)).size !== 0) {
6539
6872
  throw new UnexpectedError(spaceTrim$1((block) => `
6540
6873
  Dependent parameters are not consistent with used parameters:
6541
6874
 
@@ -6555,9 +6888,11 @@ async function executeTask(options) {
6555
6888
  }
6556
6889
  const definedParameters = Object.freeze({
6557
6890
  ...(await getReservedParametersForTask({
6891
+ tools,
6558
6892
  preparedPipeline,
6559
6893
  task: currentTask,
6560
6894
  pipelineIdentification,
6895
+ parameters: parametersToPass,
6561
6896
  })),
6562
6897
  ...parametersToPass,
6563
6898
  });
@@ -6603,6 +6938,7 @@ async function executeTask(options) {
6603
6938
  preparedPipeline,
6604
6939
  tools,
6605
6940
  $executionReport,
6941
+ onProgress,
6606
6942
  pipelineIdentification,
6607
6943
  maxExecutionAttempts,
6608
6944
  maxParallelCount,
@@ -6630,7 +6966,8 @@ async function executeTask(options) {
6630
6966
  */
6631
6967
 
6632
6968
  /**
6633
- * @@@
6969
+ * Filters and returns only the output parameters from the provided pipeline execution options.
6970
+ * Adds warnings for any expected output parameters that are missing.
6634
6971
  *
6635
6972
  * @private internal utility of `createPipelineExecutor`
6636
6973
  */
@@ -6655,9 +6992,12 @@ function filterJustOutputParameters(options) {
6655
6992
  }
6656
6993
 
6657
6994
  /**
6658
- * @@@
6995
+ * Executes an entire pipeline, resolving tasks in dependency order, handling errors, and reporting progress.
6996
+ *
6997
+ * 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
6998
  *
6660
- * Note: This is not a `PipelineExecutor` (which is binded with one exact pipeline), but a utility function of `createPipelineExecutor` which creates `PipelineExecutor`
6999
+ * @param options - Options for execution, including input parameters, pipeline, and callbacks.
7000
+ * @returns The result of the pipeline execution, including output parameters, errors, and usage statistics.
6661
7001
  *
6662
7002
  * @private internal utility of `createPipelineExecutor`
6663
7003
  */
@@ -6980,6 +7320,22 @@ function createPipelineExecutor(options) {
6980
7320
  cacheDirname,
6981
7321
  intermediateFilesStrategy,
6982
7322
  isAutoInstalled,
7323
+ }).catch((error) => {
7324
+ assertsError(error);
7325
+ return exportJson({
7326
+ name: 'pipelineExecutorResult',
7327
+ message: `Unuccessful PipelineExecutorResult, last catch`,
7328
+ order: [],
7329
+ value: {
7330
+ isSuccessful: false,
7331
+ errors: [serializeError(error)],
7332
+ warnings: [],
7333
+ usage: UNCERTAIN_USAGE,
7334
+ executionReport: null,
7335
+ outputParameters: {},
7336
+ preparedPipeline,
7337
+ },
7338
+ });
6983
7339
  });
6984
7340
  };
6985
7341
  const pipelineExecutor = (inputParameters) => createTask({
@@ -7040,27 +7396,48 @@ async function preparePersona(personaDescription, tools, options) {
7040
7396
  pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-persona.book'),
7041
7397
  tools,
7042
7398
  });
7043
- // TODO: [🚐] Make arrayable LLMs -> single LLM DRY
7044
7399
  const _llms = arrayableToArray(tools.llm);
7045
7400
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
7046
- const availableModels = await llmTools.listModels();
7047
- const availableModelNames = availableModels
7401
+ const availableModels = (await llmTools.listModels())
7048
7402
  .filter(({ modelVariant }) => modelVariant === 'CHAT')
7049
- .map(({ modelName }) => modelName)
7050
- .join(',');
7051
- const result = await preparePersonaExecutor({ availableModelNames, personaDescription }).asPromise();
7403
+ .map(({ modelName, modelDescription }) => ({
7404
+ modelName,
7405
+ modelDescription,
7406
+ // <- Note: `modelTitle` and `modelVariant` is not relevant for this task
7407
+ }));
7408
+ const result = await preparePersonaExecutor({
7409
+ availableModels /* <- Note: Passing as JSON */,
7410
+ personaDescription,
7411
+ }).asPromise();
7052
7412
  const { outputParameters } = result;
7053
- const { modelRequirements: modelRequirementsRaw } = outputParameters;
7054
- const modelRequirements = JSON.parse(modelRequirementsRaw);
7413
+ const { modelsRequirements: modelsRequirementsJson } = outputParameters;
7414
+ let modelsRequirementsUnchecked = jsonParse(modelsRequirementsJson);
7055
7415
  if (isVerbose) {
7056
- console.info(`PERSONA ${personaDescription}`, modelRequirements);
7416
+ console.info(`PERSONA ${personaDescription}`, modelsRequirementsUnchecked);
7057
7417
  }
7058
- const { modelName, systemMessage, temperature } = modelRequirements;
7059
- return {
7418
+ if (!Array.isArray(modelsRequirementsUnchecked)) {
7419
+ // <- TODO: Book should have syntax and system to enforce shape of JSON
7420
+ modelsRequirementsUnchecked = [modelsRequirementsUnchecked];
7421
+ /*
7422
+ throw new UnexpectedError(
7423
+ spaceTrim(
7424
+ (block) => `
7425
+ Invalid \`modelsRequirements\`:
7426
+
7427
+ \`\`\`json
7428
+ ${block(JSON.stringify(modelsRequirementsUnchecked, null, 4))}
7429
+ \`\`\`
7430
+ `,
7431
+ ),
7432
+ );
7433
+ */
7434
+ }
7435
+ const modelsRequirements = modelsRequirementsUnchecked.map((modelRequirements) => ({
7060
7436
  modelVariant: 'CHAT',
7061
- modelName,
7062
- systemMessage,
7063
- temperature,
7437
+ ...modelRequirements,
7438
+ }));
7439
+ return {
7440
+ modelsRequirements,
7064
7441
  };
7065
7442
  }
7066
7443
  /**
@@ -7122,7 +7499,9 @@ function mimeTypeToExtension(value) {
7122
7499
  }
7123
7500
 
7124
7501
  /**
7125
- * @@@
7502
+ * Factory function that creates a handler for processing knowledge sources.
7503
+ * Provides standardized processing of different types of knowledge sources
7504
+ * across various scraper implementations.
7126
7505
  *
7127
7506
  * @public exported from `@promptbook/core`
7128
7507
  */
@@ -7229,7 +7608,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
7229
7608
  > },
7230
7609
  */
7231
7610
  async asJson() {
7232
- return JSON.parse(await tools.fs.readFile(filename, 'utf-8'));
7611
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
7233
7612
  },
7234
7613
  async asText() {
7235
7614
  return await tools.fs.readFile(filename, 'utf-8');
@@ -7363,9 +7742,12 @@ TODO: [🧊] This is how it can look in future
7363
7742
  */
7364
7743
 
7365
7744
  /**
7366
- * @@@
7745
+ * Prepares tasks by adding knowledge to the prompt and ensuring all necessary parameters are included.
7367
7746
  *
7368
- * @public exported from `@promptbook/core`
7747
+ * @param tasks Sequence of tasks that are chained together to form a pipeline
7748
+ * @returns A promise that resolves to the prepared tasks.
7749
+ *
7750
+ * @private internal utility of `preparePipeline`
7369
7751
  */
7370
7752
  async function prepareTasks(pipeline, tools, options) {
7371
7753
  const { maxParallelCount = DEFAULT_MAX_PARALLEL_COUNT } = options;
@@ -7487,14 +7869,14 @@ async function preparePipeline(pipeline, tools, options) {
7487
7869
  // TODO: [🖌][🧠] Implement some `mapAsync` function
7488
7870
  const preparedPersonas = new Array(personas.length);
7489
7871
  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 }, {
7872
+ const { modelsRequirements } = await preparePersona(persona.description, { ...tools, llm: llmToolsWithUsage }, {
7491
7873
  rootDirname,
7492
7874
  maxParallelCount /* <- TODO: [🪂] */,
7493
7875
  isVerbose,
7494
7876
  });
7495
7877
  const preparedPersona = {
7496
7878
  ...persona,
7497
- modelRequirements,
7879
+ modelsRequirements,
7498
7880
  preparationIds: [/* TODO: [🧊] -> */ currentPreparation.id],
7499
7881
  // <- TODO: [🍙] Make some standard order of json properties
7500
7882
  };
@@ -7883,7 +8265,7 @@ const sectionCommandParser = {
7883
8265
  /**
7884
8266
  * Parses the boilerplate command
7885
8267
  *
7886
- * Note: @@@ This command is used as boilerplate for new commands - it should NOT be used in any `.book` file
8268
+ * Note: @@ This command is used as boilerplate for new commands - it should NOT be used in any `.book` file
7887
8269
  *
7888
8270
  * @see `documentationUrl` for more details
7889
8271
  * @private within the commands folder
@@ -8271,11 +8653,11 @@ const expectCommandParser = {
8271
8653
  };
8272
8654
 
8273
8655
  /**
8274
- * @@@
8656
+ * Normalizes a given text to camelCase format.
8275
8657
  *
8276
- * @param text @@@
8277
- * @param _isFirstLetterCapital @@@
8278
- * @returns @@@
8658
+ * @param text The text to be normalized.
8659
+ * @param _isFirstLetterCapital Whether the first letter should be capitalized.
8660
+ * @returns The camelCase formatted string.
8279
8661
  * @example 'helloWorld'
8280
8662
  * @example 'iLovePromptbook'
8281
8663
  * @public exported from `@promptbook/utils`
@@ -8346,11 +8728,12 @@ function removeQuotes(text) {
8346
8728
  }
8347
8729
 
8348
8730
  /**
8349
- * Function `validateParameterName` will @@@
8731
+ * Function `validateParameterName` will normalize and validate a parameter name for use in pipelines.
8732
+ * It removes diacritics, emojis, and quotes, normalizes to camelCase, and checks for reserved names and invalid characters.
8350
8733
  *
8351
- * @param parameterName @@@
8352
- * @returns @@@
8353
- * @throws {ParseError} @@@
8734
+ * @param parameterName The parameter name to validate and normalize.
8735
+ * @returns The validated and normalized parameter name.
8736
+ * @throws {ParseError} If the parameter name is empty, reserved, or contains invalid characters.
8354
8737
  * @private within the repository
8355
8738
  */
8356
8739
  function validateParameterName(parameterName) {
@@ -8420,8 +8803,6 @@ function validateParameterName(parameterName) {
8420
8803
  /**
8421
8804
  * Parses the foreach command
8422
8805
  *
8423
- * Note: @@@ This command is used as foreach for new commands - it should NOT be used in any `.book` file
8424
- *
8425
8806
  * @see `documentationUrl` for more details
8426
8807
  * @public exported from `@promptbook/editable`
8427
8808
  */
@@ -8478,14 +8859,14 @@ const foreachCommandParser = {
8478
8859
  `));
8479
8860
  // <- TODO: [🏢] List all supported format names
8480
8861
  }
8481
- const subvalueDefinition = formatDefinition.subvalueDefinitions.find((subvalueDefinition) => [subvalueDefinition.subvalueName, ...(subvalueDefinition.aliases || [])].includes(subformatName));
8482
- if (subvalueDefinition === undefined) {
8862
+ const subvalueParser = formatDefinition.subvalueParsers.find((subvalueParser) => [subvalueParser.subvalueName, ...(subvalueParser.aliases || [])].includes(subformatName));
8863
+ if (subvalueParser === undefined) {
8483
8864
  throw new ParseError(spaceTrim((block) => `
8484
8865
  Unsupported subformat name "${subformatName}" for format "${formatName}"
8485
8866
 
8486
8867
  Available subformat names for format "${formatDefinition.formatName}":
8487
- ${block(formatDefinition.subvalueDefinitions
8488
- .map((subvalueDefinition) => subvalueDefinition.subvalueName)
8868
+ ${block(formatDefinition.subvalueParsers
8869
+ .map((subvalueParser) => subvalueParser.subvalueName)
8489
8870
  .map((subvalueName) => `- ${subvalueName}`)
8490
8871
  .join('\n'))}
8491
8872
  `));
@@ -8662,14 +9043,14 @@ const formatCommandParser = {
8662
9043
  };
8663
9044
 
8664
9045
  /**
8665
- * @@@
9046
+ * Chatbot form factor definition for conversational interfaces that interact with users in a chat-like manner.
8666
9047
  *
8667
9048
  * @public exported from `@promptbook/core`
8668
9049
  */
8669
9050
  const ChatbotFormfactorDefinition = {
8670
9051
  name: 'CHATBOT',
8671
9052
  aliasNames: ['CHAT'],
8672
- description: `@@@`,
9053
+ description: `A chatbot form factor for conversational user interfaces.`,
8673
9054
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/174`,
8674
9055
  pipelineInterface: {
8675
9056
  inputParameters: [
@@ -8696,7 +9077,45 @@ const ChatbotFormfactorDefinition = {
8696
9077
  };
8697
9078
 
8698
9079
  /**
8699
- * Generator is form of app that @@@
9080
+ * Completion is formfactor that emulates completion models
9081
+ *
9082
+ * @public exported from `@promptbook/core`
9083
+ */
9084
+ const CompletionFormfactorDefinition = {
9085
+ name: 'COMPLETION',
9086
+ description: `Completion is formfactor that emulates completion models`,
9087
+ documentationUrl: `https://github.com/webgptorg/promptbook/discussions/@@`,
9088
+ // <- TODO: https://github.com/webgptorg/promptbook/discussions/new?category=concepts
9089
+ // "🔠 Completion Formfactor"
9090
+ pipelineInterface: {
9091
+ inputParameters: [
9092
+ {
9093
+ name: 'inputText',
9094
+ description: `Input text to be completed`,
9095
+ isInput: true,
9096
+ isOutput: false,
9097
+ },
9098
+ {
9099
+ name: 'instructions',
9100
+ description: `Additional instructions for the model, for example the required length, empty by default`,
9101
+ isInput: true,
9102
+ isOutput: false,
9103
+ },
9104
+ ],
9105
+ outputParameters: [
9106
+ {
9107
+ name: 'followingText',
9108
+ description: `Text that follows the input text`,
9109
+ isInput: false,
9110
+ isOutput: true,
9111
+ },
9112
+ ],
9113
+ },
9114
+ };
9115
+
9116
+ /**
9117
+ * Generator form factor represents an application that generates content or data based on user input or predefined rules.
9118
+ * This form factor is used for apps that produce outputs, such as text, images, or other media, based on provided input.
8700
9119
  *
8701
9120
  * @public exported from `@promptbook/core`
8702
9121
  */
@@ -8725,7 +9144,7 @@ const GeneratorFormfactorDefinition = {
8725
9144
  };
8726
9145
 
8727
9146
  /**
8728
- * @@@
9147
+ * Pipeline interface which is equivalent to `any`
8729
9148
  *
8730
9149
  * @see https://github.com/webgptorg/promptbook/discussions/171
8731
9150
  *
@@ -8740,13 +9159,13 @@ const GENERIC_PIPELINE_INTERFACE = {
8740
9159
  */
8741
9160
 
8742
9161
  /**
8743
- * @@@
9162
+ * A generic pipeline
8744
9163
  *
8745
9164
  * @public exported from `@promptbook/core`
8746
9165
  */
8747
9166
  const GenericFormfactorDefinition = {
8748
9167
  name: 'GENERIC',
8749
- description: `@@@`,
9168
+ description: `A generic pipeline`,
8750
9169
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/173`,
8751
9170
  pipelineInterface: GENERIC_PIPELINE_INTERFACE,
8752
9171
  };
@@ -8781,17 +9200,20 @@ const ImageGeneratorFormfactorDefinition = {
8781
9200
  };
8782
9201
 
8783
9202
  /**
8784
- * Matcher is form of app that @@@
9203
+ * Matcher is form of app that evaluates (spreadsheet) content against defined criteria or patterns,
9204
+ * determining if it matches or meets specific requirements. Used for classification,
9205
+ * validation, filtering, and quality assessment of inputs.
8785
9206
  *
8786
9207
  * @public exported from `@promptbook/core`
8787
9208
  */
8788
9209
  const MatcherFormfactorDefinition = {
8789
9210
  name: 'EXPERIMENTAL_MATCHER',
8790
- description: `@@@`,
9211
+ description: `An evaluation system that determines whether content meets specific criteria or patterns.
9212
+ Used for content validation, quality assessment, and intelligent filtering tasks. Currently in experimental phase.`,
8791
9213
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/177`,
8792
9214
  pipelineInterface: {
8793
9215
  inputParameters: [
8794
- /* @@@ */
9216
+ /* Input parameters for content to be matched and criteria to match against */
8795
9217
  {
8796
9218
  name: 'nonce',
8797
9219
  description: 'Just to prevent EXPERIMENTAL_MATCHER to be set as implicit formfactor',
@@ -8800,20 +9222,21 @@ const MatcherFormfactorDefinition = {
8800
9222
  },
8801
9223
  ],
8802
9224
  outputParameters: [
8803
- /* @@@ */
9225
+ /* Output parameters containing match results, confidence scores, and relevant metadata */
8804
9226
  ],
8805
9227
  },
8806
9228
  };
8807
9229
 
8808
9230
  /**
8809
- * Sheets is form of app that @@@
9231
+ * Sheets is form of app that processes tabular data in CSV format, allowing transformation
9232
+ * and analysis of structured data through AI-powered operations
8810
9233
  *
8811
9234
  * @public exported from `@promptbook/core`
8812
9235
  */
8813
9236
  const SheetsFormfactorDefinition = {
8814
9237
  name: 'SHEETS',
8815
9238
  aliasNames: ['SHEETS', 'SHEET'],
8816
- description: `@@@`,
9239
+ description: `A formfactor for processing spreadsheet-like data in CSV format, enabling AI transformations on tabular data`,
8817
9240
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/176`,
8818
9241
  pipelineInterface: {
8819
9242
  inputParameters: [
@@ -8836,13 +9259,16 @@ const SheetsFormfactorDefinition = {
8836
9259
  };
8837
9260
 
8838
9261
  /**
8839
- * Translator is form of app that @@@
9262
+ * Translator is form of app that transforms input text from one form to another,
9263
+ * such as language translation, style conversion, tone modification, or other text transformations.
8840
9264
  *
8841
9265
  * @public exported from `@promptbook/core`
8842
9266
  */
8843
9267
  const TranslatorFormfactorDefinition = {
8844
9268
  name: 'TRANSLATOR',
8845
- description: `@@@`,
9269
+ description: `A text transformation system that converts input content into different forms,
9270
+ including language translations, paraphrasing, style conversions, and tone adjustments.
9271
+ This form factor takes one input and produces one transformed output.`,
8846
9272
  documentationUrl: `https://github.com/webgptorg/promptbook/discussions/175`,
8847
9273
  pipelineInterface: {
8848
9274
  inputParameters: [
@@ -8879,6 +9305,8 @@ const FORMFACTOR_DEFINITIONS = [
8879
9305
  MatcherFormfactorDefinition,
8880
9306
  GeneratorFormfactorDefinition,
8881
9307
  ImageGeneratorFormfactorDefinition,
9308
+ CompletionFormfactorDefinition,
9309
+ // <- [🛬] When making new formfactor, copy the _boilerplate and link it here
8882
9310
  ];
8883
9311
  /**
8884
9312
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -8887,7 +9315,7 @@ const FORMFACTOR_DEFINITIONS = [
8887
9315
  /**
8888
9316
  * Parses the formfactor command
8889
9317
  *
8890
- * Note: @@@ This command is used as formfactor for new commands - it should NOT be used in any `.book` file
9318
+ * 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
9319
  *
8892
9320
  * @see `documentationUrl` for more details
8893
9321
  * @public exported from `@promptbook/editable`
@@ -8909,7 +9337,7 @@ const formfactorCommandParser = {
8909
9337
  /**
8910
9338
  * Description of the FORMFACTOR command
8911
9339
  */
8912
- description: `@@`,
9340
+ description: `Specifies the application type and interface requirements that this promptbook should conform to`,
8913
9341
  /**
8914
9342
  * Link to documentation
8915
9343
  */
@@ -9052,8 +9480,7 @@ const jokerCommandParser = {
9052
9480
  };
9053
9481
 
9054
9482
  /**
9055
- * @@@
9056
- *
9483
+ * @see {@link ModelVariant}
9057
9484
  * @public exported from `@promptbook/core`
9058
9485
  */
9059
9486
  const MODEL_VARIANTS = ['COMPLETION', 'CHAT', 'EMBEDDING' /* <- TODO [🏳] */ /* <- [🤖] */];
@@ -9485,10 +9912,10 @@ function $applyToTaskJson(command, $taskJson, $pipelineJson) {
9485
9912
  }
9486
9913
 
9487
9914
  /**
9488
- * @@@
9915
+ * Checks if the given value is a valid JavaScript identifier name.
9489
9916
  *
9490
- * @param javascriptName @@@
9491
- * @returns @@@
9917
+ * @param javascriptName The value to check for JavaScript identifier validity.
9918
+ * @returns `true` if the value is a valid JavaScript name, false otherwise.
9492
9919
  * @public exported from `@promptbook/utils`
9493
9920
  */
9494
9921
  function isValidJavascriptName(javascriptName) {
@@ -9968,7 +10395,10 @@ function parseCommand(raw, usagePlace) {
9968
10395
  `));
9969
10396
  }
9970
10397
  /**
9971
- * @@@
10398
+ * Generates a markdown-formatted message listing all supported commands
10399
+ * with their descriptions and documentation links
10400
+ *
10401
+ * @returns A formatted markdown string containing all available commands and their details
9972
10402
  */
9973
10403
  function getSupportedCommandsMessage() {
9974
10404
  return COMMANDS.flatMap(({ name, aliasNames, description, documentationUrl }) =>
@@ -9979,7 +10409,10 @@ function getSupportedCommandsMessage() {
9979
10409
  ]).join('\n');
9980
10410
  }
9981
10411
  /**
9982
- * @@@
10412
+ * Attempts to parse a command variant using the provided input parameters
10413
+ *
10414
+ * @param input Object containing command parsing information including raw command text and normalized values
10415
+ * @returns A parsed Command object if successful, or null if the command cannot be parsed
9983
10416
  */
9984
10417
  function parseCommandVariant(input) {
9985
10418
  const { commandNameRaw, usagePlace, normalized, args, raw, rawArgs } = input;
@@ -10026,7 +10459,7 @@ function parseCommandVariant(input) {
10026
10459
  }
10027
10460
 
10028
10461
  /**
10029
- * @@@
10462
+ * Extracts the interface (input and output parameters) from a pipeline.
10030
10463
  *
10031
10464
  * @deprecated https://github.com/webgptorg/promptbook/pull/186
10032
10465
  * @see https://github.com/webgptorg/promptbook/discussions/171
@@ -10059,7 +10492,7 @@ function getPipelineInterface(pipeline) {
10059
10492
  }
10060
10493
 
10061
10494
  /**
10062
- * @@@
10495
+ * Checks if two pipeline interfaces are structurally identical.
10063
10496
  *
10064
10497
  * @deprecated https://github.com/webgptorg/promptbook/pull/186
10065
10498
  * @see https://github.com/webgptorg/promptbook/discussions/171
@@ -10091,10 +10524,11 @@ function isPipelineInterfacesEqual(pipelineInterface1, pipelineInterface2) {
10091
10524
  }
10092
10525
 
10093
10526
  /**
10094
- * @@@
10527
+ * Checks if a given pipeline satisfies the requirements of a specified pipeline interface.
10095
10528
  *
10096
10529
  * @deprecated https://github.com/webgptorg/promptbook/pull/186
10097
10530
  * @see https://github.com/webgptorg/promptbook/discussions/171
10531
+ * @returns `true` if the pipeline implements the interface, `false` otherwise.
10098
10532
  *
10099
10533
  * @public exported from `@promptbook/core`
10100
10534
  */
@@ -10280,7 +10714,8 @@ function removeMarkdownComments(content) {
10280
10714
  }
10281
10715
 
10282
10716
  /**
10283
- * @@@
10717
+ * Utility to determine if a pipeline string is in flat format.
10718
+ * A flat pipeline is a simple text without proper structure (headers, blocks, etc).
10284
10719
  *
10285
10720
  * @public exported from `@promptbook/editable`
10286
10721
  */
@@ -10301,7 +10736,10 @@ function isFlatPipeline(pipelineString) {
10301
10736
  }
10302
10737
 
10303
10738
  /**
10304
- * @@@
10739
+ * Converts a pipeline structure to its string representation.
10740
+ *
10741
+ * Transforms a flat, simple pipeline into a properly formatted pipeline string
10742
+ * with sections for title, prompt, and return statement.
10305
10743
  *
10306
10744
  * @public exported from `@promptbook/editable`
10307
10745
  */
@@ -10358,7 +10796,7 @@ function deflatePipeline(pipelineString) {
10358
10796
  * Note: It can not work with html syntax and comments
10359
10797
  *
10360
10798
  * @param markdown any valid markdown
10361
- * @returns @@@
10799
+ * @returns An array of strings, each representing an individual list item found in the markdown
10362
10800
  * @public exported from `@promptbook/markdown-utils`
10363
10801
  */
10364
10802
  function extractAllListItemsFromMarkdown(markdown) {
@@ -11021,45 +11459,43 @@ async function compilePipeline(pipelineString, tools, options) {
11021
11459
  */
11022
11460
  function renderPromptbookMermaid(pipelineJson, options) {
11023
11461
  const { linkTask = () => null } = options || {};
11462
+ const MERMAID_PREFIX = 'pipeline_';
11463
+ const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
11464
+ const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
11465
+ const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
11466
+ const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
11024
11467
  const parameterNameToTaskName = (parameterName) => {
11468
+ if (parameterName === 'knowledge') {
11469
+ return MERMAID_KNOWLEDGE_NAME;
11470
+ }
11471
+ else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
11472
+ return MERMAID_RESERVED_NAME;
11473
+ }
11025
11474
  const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
11026
11475
  if (!parameter) {
11027
11476
  throw new UnexpectedError(`Could not find {${parameterName}}`);
11028
- // <- TODO: !!6 This causes problems when {knowledge} and other reserved parameters are used
11477
+ // <- TODO: This causes problems when {knowledge} and other reserved parameters are used
11029
11478
  }
11030
11479
  if (parameter.isInput) {
11031
- return 'input';
11480
+ return MERMAID_INPUT_NAME;
11032
11481
  }
11033
11482
  const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
11034
11483
  if (!task) {
11035
11484
  throw new Error(`Could not find task for {${parameterName}}`);
11036
11485
  }
11037
- return task.name || normalizeTo_camelCase('task-' + titleToName(task.title));
11486
+ return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
11038
11487
  };
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
11488
+ const inputAndIntermediateParametersMermaid = pipelineJson.tasks
11050
11489
  .flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
11051
11490
  `${parameterNameToTaskName(resultingParameterName)}("${title}")`,
11052
11491
  ...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
11053
11492
  ])
11054
- .join('\n'))}
11055
-
11056
- ${block(pipelineJson.parameters
11493
+ .join('\n');
11494
+ const outputParametersMermaid = pipelineJson.parameters
11057
11495
  .filter(({ isOutput }) => isOutput)
11058
- .map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->output`)
11059
- .join('\n'))}
11060
- output((Output)):::output
11061
-
11062
- ${block(pipelineJson.tasks
11496
+ .map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
11497
+ .join('\n');
11498
+ const linksMermaid = pipelineJson.tasks
11063
11499
  .map((task) => {
11064
11500
  const link = linkTask(task);
11065
11501
  if (link === null) {
@@ -11070,10 +11506,44 @@ function renderPromptbookMermaid(pipelineJson, options) {
11070
11506
  return `click ${taskName} href "${href}" "${title}";`;
11071
11507
  })
11072
11508
  .filter((line) => line !== '')
11073
- .join('\n'))}
11509
+ .join('\n');
11510
+ const interactionPointsMermaid = Object.entries({
11511
+ [MERMAID_INPUT_NAME]: 'Input',
11512
+ [MERMAID_OUTPUT_NAME]: 'Output',
11513
+ [MERMAID_RESERVED_NAME]: 'Other',
11514
+ [MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
11515
+ })
11516
+ .filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
11517
+ .map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
11518
+ .join('\n');
11519
+ const promptbookMermaid = spaceTrim$1((block) => `
11520
+
11521
+ %% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
11522
+
11523
+ flowchart LR
11524
+ subgraph "${pipelineJson.title}"
11525
+
11526
+ %% Basic configuration
11527
+ direction TB
11528
+
11529
+ %% Interaction points from pipeline to outside
11530
+ ${block(interactionPointsMermaid)}
11531
+
11532
+ %% Input and intermediate parameters
11533
+ ${block(inputAndIntermediateParametersMermaid)}
11074
11534
 
11075
- classDef input color: grey;
11076
- classDef output color: grey;
11535
+
11536
+ %% Output parameters
11537
+ ${block(outputParametersMermaid)}
11538
+
11539
+ %% Links
11540
+ ${block(linksMermaid)}
11541
+
11542
+ %% Styles
11543
+ classDef ${MERMAID_INPUT_NAME} color: grey;
11544
+ classDef ${MERMAID_OUTPUT_NAME} color: grey;
11545
+ classDef ${MERMAID_RESERVED_NAME} color: grey;
11546
+ classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
11077
11547
 
11078
11548
  end;
11079
11549
 
@@ -11113,11 +11583,11 @@ function parseKeywordsFromString(input) {
11113
11583
  }
11114
11584
 
11115
11585
  /**
11116
- * @@@
11586
+ * Converts a name string into a URI-compatible format.
11117
11587
  *
11118
- * @param name @@@
11119
- * @returns @@@
11120
- * @example @@@
11588
+ * @param name The string to be converted to a URI-compatible format.
11589
+ * @returns A URI-compatible string derived from the input name.
11590
+ * @example 'Hello World' -> 'hello-world'
11121
11591
  * @public exported from `@promptbook/utils`
11122
11592
  */
11123
11593
  function nameToUriPart(name) {
@@ -11131,11 +11601,11 @@ function nameToUriPart(name) {
11131
11601
  }
11132
11602
 
11133
11603
  /**
11134
- * @@@
11604
+ * Converts a given name into URI-compatible parts.
11135
11605
  *
11136
- * @param name @@@
11137
- * @returns @@@
11138
- * @example @@@
11606
+ * @param name The name to be converted into URI parts.
11607
+ * @returns An array of URI-compatible parts derived from the name.
11608
+ * @example 'Example Name' -> ['example', 'name']
11139
11609
  * @public exported from `@promptbook/utils`
11140
11610
  */
11141
11611
  function nameToUriParts(name) {
@@ -12934,7 +13404,7 @@ function $initializeRunCommand(program) {
12934
13404
  }
12935
13405
  let inputParameters = {};
12936
13406
  if (json) {
12937
- inputParameters = JSON.parse(json);
13407
+ inputParameters = jsonParse(json);
12938
13408
  // <- TODO: Maybe check shape of passed JSON and if its valid parameters Record
12939
13409
  }
12940
13410
  // TODO: DRY [◽]
@@ -13111,10 +13581,10 @@ function $initializeRunCommand(program) {
13111
13581
  console.info(colors.gray('--- Detailed Result ---'));
13112
13582
  console.info({ isSuccessful, errors, warnings, outputParameters, executionReport });
13113
13583
  }
13114
- if (saveReport && saveReport.endsWith('.json')) {
13584
+ if (executionReport !== null && saveReport && saveReport.endsWith('.json')) {
13115
13585
  await writeFile(saveReport, JSON.stringify(executionReport, null, 4) + '\n', 'utf-8');
13116
13586
  }
13117
- else if (saveReport && saveReport.endsWith('.md')) {
13587
+ else if (executionReport !== null && saveReport && saveReport.endsWith('.md')) {
13118
13588
  const executionReportString = executionReportJsonToString(executionReport);
13119
13589
  await writeFile(saveReport, executionReportString, 'utf-8');
13120
13590
  }
@@ -13157,15 +13627,15 @@ function $initializeRunCommand(program) {
13157
13627
  * TODO: [🖇] What about symlinks? Maybe flag --follow-symlinks
13158
13628
  */
13159
13629
 
13160
- // TODO: !!!! List running services from REMOTE_SERVER_URLS
13161
- // TODO: !!!! Import directly from YML
13630
+ // TODO: [🥺] List running services from REMOTE_SERVER_URLS
13631
+ // TODO: [🥺] Import directly from YML
13162
13632
  /**
13163
- * @private !!!! Decide how to expose this
13633
+ * @private [🥺] Decide how to expose this
13164
13634
  */
13165
13635
  const openapiJson = {
13166
13636
  openapi: '3.0.0',
13167
13637
  info: {
13168
- title: 'Promptbook Remote Server API (!!!! From TS)',
13638
+ title: 'Promptbook Remote Server API ([🥺] From YML)',
13169
13639
  version: '1.0.0',
13170
13640
  description: 'API documentation for the Promptbook Remote Server',
13171
13641
  },
@@ -13177,6 +13647,13 @@ const openapiJson = {
13177
13647
  responses: {
13178
13648
  '200': {
13179
13649
  description: 'Server details in markdown format.',
13650
+ content: {
13651
+ 'text/markdown': {
13652
+ schema: {
13653
+ type: 'string',
13654
+ },
13655
+ },
13656
+ },
13180
13657
  },
13181
13658
  },
13182
13659
  },
@@ -13207,13 +13684,22 @@ const openapiJson = {
13207
13684
  },
13208
13685
  },
13209
13686
  responses: {
13210
- '200': {
13687
+ '201': {
13211
13688
  description: 'Successful login',
13212
13689
  content: {
13213
13690
  'application/json': {
13214
13691
  schema: {
13215
13692
  type: 'object',
13216
13693
  properties: {
13694
+ isSuccess: {
13695
+ type: 'boolean',
13696
+ },
13697
+ message: {
13698
+ type: 'string',
13699
+ },
13700
+ error: {
13701
+ type: 'object',
13702
+ },
13217
13703
  identification: {
13218
13704
  type: 'object',
13219
13705
  },
@@ -13222,6 +13708,43 @@ const openapiJson = {
13222
13708
  },
13223
13709
  },
13224
13710
  },
13711
+ '400': {
13712
+ description: 'Bad request or login failed',
13713
+ content: {
13714
+ 'application/json': {
13715
+ schema: {
13716
+ type: 'object',
13717
+ properties: {
13718
+ error: {
13719
+ type: 'object',
13720
+ },
13721
+ },
13722
+ },
13723
+ },
13724
+ },
13725
+ },
13726
+ '401': {
13727
+ description: 'Authentication error',
13728
+ content: {
13729
+ 'application/json': {
13730
+ schema: {
13731
+ type: 'object',
13732
+ properties: {
13733
+ isSuccess: {
13734
+ type: 'boolean',
13735
+ enum: [false],
13736
+ },
13737
+ message: {
13738
+ type: 'string',
13739
+ },
13740
+ error: {
13741
+ type: 'object',
13742
+ },
13743
+ },
13744
+ },
13745
+ },
13746
+ },
13747
+ },
13225
13748
  },
13226
13749
  },
13227
13750
  },
@@ -13243,6 +13766,16 @@ const openapiJson = {
13243
13766
  },
13244
13767
  },
13245
13768
  },
13769
+ '500': {
13770
+ description: 'No collection available',
13771
+ content: {
13772
+ 'text/plain': {
13773
+ schema: {
13774
+ type: 'string',
13775
+ },
13776
+ },
13777
+ },
13778
+ },
13246
13779
  },
13247
13780
  },
13248
13781
  },
@@ -13274,6 +13807,28 @@ const openapiJson = {
13274
13807
  },
13275
13808
  '404': {
13276
13809
  description: 'Book not found.',
13810
+ content: {
13811
+ 'application/json': {
13812
+ schema: {
13813
+ type: 'object',
13814
+ properties: {
13815
+ error: {
13816
+ type: 'object',
13817
+ },
13818
+ },
13819
+ },
13820
+ },
13821
+ },
13822
+ },
13823
+ '500': {
13824
+ description: 'No collection available',
13825
+ content: {
13826
+ 'text/plain': {
13827
+ schema: {
13828
+ type: 'string',
13829
+ },
13830
+ },
13831
+ },
13277
13832
  },
13278
13833
  },
13279
13834
  },
@@ -13291,11 +13846,174 @@ const openapiJson = {
13291
13846
  type: 'array',
13292
13847
  items: {
13293
13848
  type: 'object',
13849
+ properties: {
13850
+ nonce: {
13851
+ type: 'string',
13852
+ },
13853
+ taskId: {
13854
+ type: 'string',
13855
+ },
13856
+ taskType: {
13857
+ type: 'string',
13858
+ },
13859
+ status: {
13860
+ type: 'string',
13861
+ },
13862
+ createdAt: {
13863
+ type: 'string',
13864
+ format: 'date-time',
13865
+ },
13866
+ updatedAt: {
13867
+ type: 'string',
13868
+ format: 'date-time',
13869
+ },
13870
+ },
13871
+ },
13872
+ },
13873
+ },
13874
+ },
13875
+ },
13876
+ },
13877
+ },
13878
+ },
13879
+ '/executions/last': {
13880
+ get: {
13881
+ summary: 'Get the last execution',
13882
+ description: 'Returns details of the last execution task.',
13883
+ responses: {
13884
+ '200': {
13885
+ description: 'The last execution task with full details.',
13886
+ content: {
13887
+ 'application/json': {
13888
+ schema: {
13889
+ type: 'object',
13890
+ properties: {
13891
+ nonce: {
13892
+ type: 'string',
13893
+ },
13894
+ taskId: {
13895
+ type: 'string',
13896
+ },
13897
+ taskType: {
13898
+ type: 'string',
13899
+ },
13900
+ status: {
13901
+ type: 'string',
13902
+ },
13903
+ errors: {
13904
+ type: 'array',
13905
+ items: {
13906
+ type: 'object',
13907
+ },
13908
+ },
13909
+ warnings: {
13910
+ type: 'array',
13911
+ items: {
13912
+ type: 'object',
13913
+ },
13914
+ },
13915
+ createdAt: {
13916
+ type: 'string',
13917
+ format: 'date-time',
13918
+ },
13919
+ updatedAt: {
13920
+ type: 'string',
13921
+ format: 'date-time',
13922
+ },
13923
+ currentValue: {
13924
+ type: 'object',
13925
+ },
13926
+ },
13927
+ },
13928
+ },
13929
+ },
13930
+ },
13931
+ '404': {
13932
+ description: 'No execution tasks found.',
13933
+ content: {
13934
+ 'text/plain': {
13935
+ schema: {
13936
+ type: 'string',
13937
+ },
13938
+ },
13939
+ },
13940
+ },
13941
+ },
13942
+ },
13943
+ },
13944
+ '/executions/{taskId}': {
13945
+ get: {
13946
+ summary: 'Get specific execution',
13947
+ description: 'Returns details of a specific execution task.',
13948
+ parameters: [
13949
+ {
13950
+ in: 'path',
13951
+ name: 'taskId',
13952
+ required: true,
13953
+ schema: {
13954
+ type: 'string',
13955
+ },
13956
+ description: 'The ID of the execution task to retrieve.',
13957
+ },
13958
+ ],
13959
+ responses: {
13960
+ '200': {
13961
+ description: 'The execution task with full details.',
13962
+ content: {
13963
+ 'application/json': {
13964
+ schema: {
13965
+ type: 'object',
13966
+ properties: {
13967
+ nonce: {
13968
+ type: 'string',
13969
+ },
13970
+ taskId: {
13971
+ type: 'string',
13972
+ },
13973
+ taskType: {
13974
+ type: 'string',
13975
+ },
13976
+ status: {
13977
+ type: 'string',
13978
+ },
13979
+ errors: {
13980
+ type: 'array',
13981
+ items: {
13982
+ type: 'object',
13983
+ },
13984
+ },
13985
+ warnings: {
13986
+ type: 'array',
13987
+ items: {
13988
+ type: 'object',
13989
+ },
13990
+ },
13991
+ createdAt: {
13992
+ type: 'string',
13993
+ format: 'date-time',
13994
+ },
13995
+ updatedAt: {
13996
+ type: 'string',
13997
+ format: 'date-time',
13998
+ },
13999
+ currentValue: {
14000
+ type: 'object',
14001
+ },
13294
14002
  },
13295
14003
  },
13296
14004
  },
13297
14005
  },
13298
14006
  },
14007
+ '404': {
14008
+ description: 'Execution task not found.',
14009
+ content: {
14010
+ 'text/plain': {
14011
+ schema: {
14012
+ type: 'string',
14013
+ },
14014
+ },
14015
+ },
14016
+ },
13299
14017
  },
13300
14018
  },
13301
14019
  },
@@ -13312,12 +14030,19 @@ const openapiJson = {
13312
14030
  properties: {
13313
14031
  pipelineUrl: {
13314
14032
  type: 'string',
14033
+ description: 'URL of the pipeline to execute',
14034
+ },
14035
+ book: {
14036
+ type: 'string',
14037
+ description: 'Alternative field for pipelineUrl',
13315
14038
  },
13316
14039
  inputParameters: {
13317
14040
  type: 'object',
14041
+ description: 'Parameters for pipeline execution',
13318
14042
  },
13319
14043
  identification: {
13320
14044
  type: 'object',
14045
+ description: 'User identification data',
13321
14046
  },
13322
14047
  },
13323
14048
  },
@@ -13337,13 +14062,164 @@ const openapiJson = {
13337
14062
  },
13338
14063
  '400': {
13339
14064
  description: 'Invalid input.',
14065
+ content: {
14066
+ 'application/json': {
14067
+ schema: {
14068
+ type: 'object',
14069
+ properties: {
14070
+ error: {
14071
+ type: 'object',
14072
+ },
14073
+ },
14074
+ },
14075
+ },
14076
+ },
14077
+ },
14078
+ '404': {
14079
+ description: 'Pipeline not found.',
14080
+ content: {
14081
+ 'text/plain': {
14082
+ schema: {
14083
+ type: 'string',
14084
+ },
14085
+ },
14086
+ },
14087
+ },
14088
+ },
14089
+ },
14090
+ },
14091
+ '/api-docs': {
14092
+ get: {
14093
+ summary: 'API documentation UI',
14094
+ description: 'Swagger UI for API documentation',
14095
+ responses: {
14096
+ '200': {
14097
+ description: 'HTML Swagger UI',
14098
+ },
14099
+ },
14100
+ },
14101
+ },
14102
+ '/swagger': {
14103
+ get: {
14104
+ summary: 'API documentation UI (alternative path)',
14105
+ description: 'Swagger UI for API documentation',
14106
+ responses: {
14107
+ '200': {
14108
+ description: 'HTML Swagger UI',
14109
+ },
14110
+ },
14111
+ },
14112
+ },
14113
+ '/openapi': {
14114
+ get: {
14115
+ summary: 'OpenAPI specification',
14116
+ description: 'Returns the OpenAPI JSON specification',
14117
+ responses: {
14118
+ '200': {
14119
+ description: 'OpenAPI specification',
14120
+ content: {
14121
+ 'application/json': {
14122
+ schema: {
14123
+ type: 'object',
14124
+ },
14125
+ },
14126
+ },
14127
+ },
14128
+ },
14129
+ },
14130
+ },
14131
+ },
14132
+ components: {
14133
+ schemas: {
14134
+ Error: {
14135
+ type: 'object',
14136
+ properties: {
14137
+ error: {
14138
+ type: 'object',
14139
+ },
14140
+ },
14141
+ },
14142
+ ExecutionTaskSummary: {
14143
+ type: 'object',
14144
+ properties: {
14145
+ nonce: {
14146
+ type: 'string',
14147
+ },
14148
+ taskId: {
14149
+ type: 'string',
14150
+ },
14151
+ taskType: {
14152
+ type: 'string',
14153
+ },
14154
+ status: {
14155
+ type: 'string',
14156
+ },
14157
+ createdAt: {
14158
+ type: 'string',
14159
+ format: 'date-time',
14160
+ },
14161
+ updatedAt: {
14162
+ type: 'string',
14163
+ format: 'date-time',
14164
+ },
14165
+ },
14166
+ },
14167
+ ExecutionTaskFull: {
14168
+ type: 'object',
14169
+ properties: {
14170
+ nonce: {
14171
+ type: 'string',
14172
+ },
14173
+ taskId: {
14174
+ type: 'string',
14175
+ },
14176
+ taskType: {
14177
+ type: 'string',
14178
+ },
14179
+ status: {
14180
+ type: 'string',
14181
+ },
14182
+ errors: {
14183
+ type: 'array',
14184
+ items: {
14185
+ type: 'object',
14186
+ },
14187
+ },
14188
+ warnings: {
14189
+ type: 'array',
14190
+ items: {
14191
+ type: 'object',
14192
+ },
14193
+ },
14194
+ createdAt: {
14195
+ type: 'string',
14196
+ format: 'date-time',
14197
+ },
14198
+ updatedAt: {
14199
+ type: 'string',
14200
+ format: 'date-time',
14201
+ },
14202
+ currentValue: {
14203
+ type: 'object',
13340
14204
  },
13341
14205
  },
13342
14206
  },
13343
14207
  },
13344
14208
  },
13345
- components: {},
13346
- tags: [],
14209
+ tags: [
14210
+ {
14211
+ name: 'Books',
14212
+ description: 'Operations related to books and pipelines',
14213
+ },
14214
+ {
14215
+ name: 'Executions',
14216
+ description: 'Operations related to execution tasks',
14217
+ },
14218
+ {
14219
+ name: 'Authentication',
14220
+ description: 'Authentication operations',
14221
+ },
14222
+ ],
13347
14223
  };
13348
14224
  /**
13349
14225
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -13417,7 +14293,7 @@ function startRemoteServer(options) {
13417
14293
  response.setHeader('X-Powered-By', 'Promptbook engine');
13418
14294
  next();
13419
14295
  });
13420
- // TODO: !!!! Expose openapiJson to consumer and also allow to add new routes
14296
+ // TODO: [🥺] Expose openapiJson to consumer and also allow to add new routes
13421
14297
  app.use(OpenApiValidator.middleware({
13422
14298
  apiSpec: openapiJson,
13423
14299
  ignorePaths(path) {
@@ -13714,6 +14590,7 @@ function startRemoteServer(options) {
13714
14590
  promptResult = await llm.callCompletionModel(prompt);
13715
14591
  break;
13716
14592
  case 'EMBEDDING':
14593
+ console.log('!!! llm (EMBEDDING)', llm);
13717
14594
  if (llm.callEmbeddingModel === undefined) {
13718
14595
  // Note: [0] This check should not be a thing
13719
14596
  throw new PipelineExecutionError(`Embedding model is not available`);
@@ -13988,7 +14865,7 @@ function $initializeTestCommand(program) {
13988
14865
  }
13989
14866
  }
13990
14867
  if (filename.endsWith('.bookc')) {
13991
- pipeline = JSON.parse(await readFile(filename, 'utf-8'));
14868
+ pipeline = jsonParse(await readFile(filename, 'utf-8'));
13992
14869
  }
13993
14870
  else {
13994
14871
  if (isVerbose) {
@@ -14111,9 +14988,11 @@ const _AnthropicClaudeMetadataRegistration = $llmToolsMetadataRegister.register(
14111
14988
  packageName: '@promptbook/anthropic-claude',
14112
14989
  className: 'AnthropicClaudeExecutionTools',
14113
14990
  envVariables: ['ANTHROPIC_CLAUDE_API_KEY'],
14991
+ trustLevel: 'CLOSED',
14992
+ order: MODEL_ORDERS.TOP_TIER,
14114
14993
  getBoilerplateConfiguration() {
14115
14994
  return {
14116
- title: 'Anthropic Claude (boilerplate)',
14995
+ title: 'Anthropic Claude',
14117
14996
  packageName: '@promptbook/anthropic-claude',
14118
14997
  className: 'AnthropicClaudeExecutionTools',
14119
14998
  options: {
@@ -14155,7 +15034,7 @@ function computeUsage(value) {
14155
15034
  /**
14156
15035
  * List of available Anthropic Claude models with pricing
14157
15036
  *
14158
- * Note: Done at 2024-08-16
15037
+ * Note: Done at 2025-05-06
14159
15038
  *
14160
15039
  * @see https://docs.anthropic.com/en/docs/models-overview
14161
15040
  * @public exported from `@promptbook/anthropic-claude`
@@ -14169,8 +15048,8 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
14169
15048
  modelName: 'claude-3-5-sonnet-20240620',
14170
15049
  modelDescription: 'Latest Claude model with great reasoning, coding, and language understanding capabilities. 200K context window. Optimized balance of intelligence and speed.',
14171
15050
  pricing: {
14172
- prompt: computeUsage(`$3.00 / 1M tokens`),
14173
- output: computeUsage(`$15.00 / 1M tokens`),
15051
+ prompt: computeUsage(`$2.50 / 1M tokens`),
15052
+ output: computeUsage(`$12.50 / 1M tokens`),
14174
15053
  },
14175
15054
  },
14176
15055
  {
@@ -14179,8 +15058,8 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
14179
15058
  modelName: 'claude-3-opus-20240229',
14180
15059
  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
15060
  pricing: {
14182
- prompt: computeUsage(`$15.00 / 1M tokens`),
14183
- output: computeUsage(`$75.00 / 1M tokens`),
15061
+ prompt: computeUsage(`$12.00 / 1M tokens`),
15062
+ output: computeUsage(`$60.00 / 1M tokens`),
14184
15063
  },
14185
15064
  },
14186
15065
  {
@@ -14225,7 +15104,7 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
14225
15104
  },
14226
15105
  {
14227
15106
  modelVariant: 'CHAT',
14228
- modelTitle: ' Claude Instant 1.2',
15107
+ modelTitle: 'Claude Instant 1.2',
14229
15108
  modelName: 'claude-instant-1.2',
14230
15109
  modelDescription: 'Older, faster Claude model optimized for high throughput applications. Lower cost but less capable than newer models. 100K context window.',
14231
15110
  pricing: {
@@ -14239,8 +15118,8 @@ const ANTHROPIC_CLAUDE_MODELS = exportJson({
14239
15118
  modelName: 'claude-3-7-sonnet-20250219',
14240
15119
  modelDescription: 'Latest generation Claude model with advanced reasoning and language understanding. Enhanced capabilities over 3.5 with improved domain knowledge. 200K context window.',
14241
15120
  pricing: {
14242
- prompt: computeUsage(`$3.00 / 1M tokens`),
14243
- output: computeUsage(`$15.00 / 1M tokens`),
15121
+ prompt: computeUsage(`$2.50 / 1M tokens`),
15122
+ output: computeUsage(`$12.50 / 1M tokens`),
14244
15123
  },
14245
15124
  },
14246
15125
  {
@@ -14287,14 +15166,18 @@ function computeUsageCounts(content) {
14287
15166
  /**
14288
15167
  * Make UncertainNumber
14289
15168
  *
14290
- * @param value
15169
+ * @param value value of the uncertain number, if `NaN` or `undefined`, it will be set to 0 and `isUncertain=true`
15170
+ * @param isUncertain if `true`, the value is uncertain, otherwise depends on the value
14291
15171
  *
14292
15172
  * @private utility for initializating UncertainNumber
14293
15173
  */
14294
- function uncertainNumber(value) {
15174
+ function uncertainNumber(value, isUncertain) {
14295
15175
  if (value === null || value === undefined || Number.isNaN(value)) {
14296
15176
  return UNCERTAIN_ZERO_VALUE;
14297
15177
  }
15178
+ if (isUncertain === true) {
15179
+ return { value, isUncertain };
15180
+ }
14298
15181
  return { value };
14299
15182
  }
14300
15183
 
@@ -14649,9 +15532,11 @@ const _AzureOpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
14649
15532
  packageName: '@promptbook/azure-openai',
14650
15533
  className: 'AzureOpenAiExecutionTools',
14651
15534
  envVariables: ['AZUREOPENAI_RESOURCE_NAME', 'AZUREOPENAI_DEPLOYMENT_NAME', 'AZUREOPENAI_API_KEY'],
15535
+ trustLevel: 'CLOSED_BUSINESS',
15536
+ order: MODEL_ORDERS.NORMAL,
14652
15537
  getBoilerplateConfiguration() {
14653
15538
  return {
14654
- title: 'Azure Open AI (boilerplate)',
15539
+ title: 'Azure Open AI',
14655
15540
  packageName: '@promptbook/azure-openai',
14656
15541
  className: 'AzureOpenAiExecutionTools',
14657
15542
  options: {
@@ -14706,7 +15591,7 @@ const _AzureOpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
14706
15591
  /**
14707
15592
  * List of available OpenAI models with pricing
14708
15593
  *
14709
- * Note: Done at 2024-05-20
15594
+ * Note: Done at 2025-05-06
14710
15595
  *
14711
15596
  * @see https://platform.openai.com/docs/models/
14712
15597
  * @see https://openai.com/api/pricing/
@@ -14735,7 +15620,7 @@ const OPENAI_MODELS = exportJson({
14735
15620
  modelDescription: 'Legacy completion model with strong performance on text generation tasks. Optimized for complex instructions and longer outputs.',
14736
15621
  pricing: {
14737
15622
  prompt: computeUsage(`$2.00 / 1M tokens`),
14738
- output: computeUsage(`$2.00 / 1M tokens`), // <- not sure
15623
+ output: computeUsage(`$2.00 / 1M tokens`),
14739
15624
  },
14740
15625
  },
14741
15626
  /**/
@@ -14877,8 +15762,8 @@ const OPENAI_MODELS = exportJson({
14877
15762
  modelName: 'gpt-3.5-turbo',
14878
15763
  modelDescription: 'Latest version of GPT-3.5 Turbo with improved performance and instruction following capabilities. Default 4K context window with options for 16K.',
14879
15764
  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
15765
+ prompt: computeUsage(`$0.50 / 1M tokens`),
15766
+ output: computeUsage(`$1.50 / 1M tokens`),
14882
15767
  },
14883
15768
  },
14884
15769
  /**/
@@ -14902,7 +15787,7 @@ const OPENAI_MODELS = exportJson({
14902
15787
  modelDescription: 'Efficient legacy completion model with a good balance of performance and speed. Suitable for straightforward text generation tasks.',
14903
15788
  pricing: {
14904
15789
  prompt: computeUsage(`$0.40 / 1M tokens`),
14905
- output: computeUsage(`$0.40 / 1M tokens`), // <- Not sure
15790
+ output: computeUsage(`$0.40 / 1M tokens`),
14906
15791
  },
14907
15792
  },
14908
15793
  /**/
@@ -14956,7 +15841,7 @@ const OPENAI_MODELS = exportJson({
14956
15841
  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
15842
  pricing: {
14958
15843
  prompt: computeUsage(`$10.00 / 1M tokens`),
14959
- output: computeUsage(`$30.00 / 1M tokens`), // <- Not sure, just for gpt-4-turbo
15844
+ output: computeUsage(`$30.00 / 1M tokens`),
14960
15845
  },
14961
15846
  },
14962
15847
  /**/
@@ -14967,7 +15852,7 @@ const OPENAI_MODELS = exportJson({
14967
15852
  modelName: 'text-embedding-3-large',
14968
15853
  modelDescription: "OpenAI's most capable text embedding model designed for high-quality embeddings for complex similarity tasks and information retrieval.",
14969
15854
  pricing: {
14970
- prompt: computeUsage(`$0.13 / 1M tokens`),
15855
+ prompt: computeUsage(`$0.13 / 1M tokens`),
14971
15856
  // TODO: [🏏] Leverage the batch API @see https://platform.openai.com/docs/guides/batch
14972
15857
  output: 0, // <- Note: [🆖] In Embedding models you dont pay for output
14973
15858
  },
@@ -15060,8 +15945,8 @@ const OPENAI_MODELS = exportJson({
15060
15945
  modelName: 'gpt-4o-mini',
15061
15946
  modelDescription: 'Smaller, more cost-effective version of GPT-4o with good performance across text, vision, and audio tasks at reduced complexity.',
15062
15947
  pricing: {
15063
- prompt: computeUsage(`$3.00 / 1M tokens`),
15064
- output: computeUsage(`$9.00 / 1M tokens`),
15948
+ prompt: computeUsage(`$0.15 / 1M tokens`),
15949
+ output: computeUsage(`$0.60 / 1M tokens`),
15065
15950
  },
15066
15951
  },
15067
15952
  /**/
@@ -15107,10 +15992,10 @@ const OPENAI_MODELS = exportJson({
15107
15992
  modelVariant: 'CHAT',
15108
15993
  modelTitle: 'o1',
15109
15994
  modelName: 'o1',
15995
+ 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
15996
  pricing: {
15111
- prompt: computeUsage(`$3.00 / 1M tokens`),
15112
- output: computeUsage(`$12.00 / 1M tokens`),
15113
- // <- TODO: !! Unsure, check the pricing
15997
+ prompt: computeUsage(`$15.00 / 1M tokens`),
15998
+ output: computeUsage(`$60.00 / 1M tokens`),
15114
15999
  },
15115
16000
  },
15116
16001
  /**/
@@ -15119,7 +16004,7 @@ const OPENAI_MODELS = exportJson({
15119
16004
  modelVariant: 'CHAT',
15120
16005
  modelTitle: 'o3-mini',
15121
16006
  modelName: 'o3-mini',
15122
- modelDescription: 'Compact and efficient reasoning model specializing in problem-solving with a focus on research and analysis tasks.',
16007
+ 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
16008
  pricing: {
15124
16009
  prompt: computeUsage(`$3.00 / 1M tokens`),
15125
16010
  output: computeUsage(`$12.00 / 1M tokens`),
@@ -15186,6 +16071,10 @@ class AzureOpenAiExecutionTools {
15186
16071
  * OpenAI Azure API client.
15187
16072
  */
15188
16073
  this.client = null;
16074
+ // TODO: Allow configuring rate limits via options
16075
+ this.limiter = new Bottleneck({
16076
+ minTime: 60000 / (this.options.maxRequestsPerMinute || DEFAULT_MAX_REQUESTS_PER_MINUTE),
16077
+ });
15189
16078
  }
15190
16079
  get title() {
15191
16080
  return 'Azure OpenAI';
@@ -15263,7 +16152,9 @@ class AzureOpenAiExecutionTools {
15263
16152
  console.info(colors.bgWhite('messages'), JSON.stringify(messages, null, 4));
15264
16153
  }
15265
16154
  const rawRequest = [modelName, messages, modelSettings];
15266
- const rawResponse = await this.withTimeout(client.getChatCompletions(...rawRequest)).catch((error) => {
16155
+ const rawResponse = await this.limiter
16156
+ .schedule(() => this.withTimeout(client.getChatCompletions(...rawRequest)))
16157
+ .catch((error) => {
15267
16158
  if (this.options.isVerbose) {
15268
16159
  console.info(colors.bgRed('error'), error);
15269
16160
  }
@@ -15359,7 +16250,9 @@ class AzureOpenAiExecutionTools {
15359
16250
  [rawPromptContent],
15360
16251
  modelSettings,
15361
16252
  ];
15362
- const rawResponse = await this.withTimeout(client.getCompletions(...rawRequest)).catch((error) => {
16253
+ const rawResponse = await this.limiter
16254
+ .schedule(() => this.withTimeout(client.getCompletions(...rawRequest)))
16255
+ .catch((error) => {
15363
16256
  if (this.options.isVerbose) {
15364
16257
  console.info(colors.bgRed('error'), error);
15365
16258
  }
@@ -15499,9 +16392,11 @@ const _DeepseekMetadataRegistration = $llmToolsMetadataRegister.register({
15499
16392
  packageName: '@promptbook/deepseek',
15500
16393
  className: 'DeepseekExecutionTools',
15501
16394
  envVariables: ['DEEPSEEK_GENERATIVE_AI_API_KEY'],
16395
+ trustLevel: 'UNTRUSTED',
16396
+ order: MODEL_ORDERS.NORMAL,
15502
16397
  getBoilerplateConfiguration() {
15503
16398
  return {
15504
- title: 'Deepseek (boilerplate)',
16399
+ title: 'Deepseek',
15505
16400
  packageName: '@promptbook/deepseek',
15506
16401
  className: 'DeepseekExecutionTools',
15507
16402
  options: {
@@ -15698,6 +16593,67 @@ function createExecutionToolsFromVercelProvider(options) {
15698
16593
  };
15699
16594
  }
15700
16595
 
16596
+ /**
16597
+ * List of available Deepseek models with descriptions
16598
+ *
16599
+ * Note: Done at 2025-05-06
16600
+ *
16601
+ * @see https://www.deepseek.com/models
16602
+ * @public exported from `@promptbook/deepseek`
16603
+ */
16604
+ const DEEPSEEK_MODELS = exportJson({
16605
+ name: 'DEEPSEEK_MODELS',
16606
+ value: [
16607
+ {
16608
+ modelVariant: 'CHAT',
16609
+ modelTitle: 'Deepseek Chat',
16610
+ modelName: 'deepseek-chat',
16611
+ modelDescription: 'General-purpose language model with strong performance across conversation, reasoning, and content generation. 128K context window with excellent instruction following capabilities.',
16612
+ pricing: {
16613
+ prompt: computeUsage(`$0.80 / 1M tokens`),
16614
+ output: computeUsage(`$1.60 / 1M tokens`),
16615
+ },
16616
+ },
16617
+ {
16618
+ modelVariant: 'CHAT',
16619
+ modelTitle: 'Deepseek Reasoner',
16620
+ modelName: 'deepseek-reasoner',
16621
+ 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.',
16622
+ pricing: {
16623
+ prompt: computeUsage(`$3.50 / 1M tokens`),
16624
+ output: computeUsage(`$7.00 / 1M tokens`),
16625
+ },
16626
+ },
16627
+ {
16628
+ modelVariant: 'CHAT',
16629
+ modelTitle: 'DeepSeek V3',
16630
+ modelName: 'deepseek-v3-0324',
16631
+ modelDescription: 'Advanced general-purpose model with improved reasoning, coding abilities, and multimodal understanding. Built on the latest DeepSeek architecture with enhanced knowledge representation.',
16632
+ pricing: {
16633
+ prompt: computeUsage(`$1.50 / 1M tokens`),
16634
+ output: computeUsage(`$3.00 / 1M tokens`),
16635
+ },
16636
+ },
16637
+ {
16638
+ modelVariant: 'CHAT',
16639
+ modelTitle: 'DeepSeek R1',
16640
+ modelName: 'deepseek-r1',
16641
+ modelDescription: 'Research-focused model optimized for scientific problem-solving and analytical tasks. Excellent performance on tasks requiring domain-specific expertise and critical thinking.',
16642
+ pricing: {
16643
+ prompt: computeUsage(`$5.00 / 1M tokens`),
16644
+ output: computeUsage(`$10.00 / 1M tokens`),
16645
+ },
16646
+ },
16647
+ // <- [🕕]
16648
+ ],
16649
+ });
16650
+ /**
16651
+ * TODO: [🧠] Add information about context window sizes, capabilities, and relative performance characteristics
16652
+ * TODO: [🎰] Some mechanism to auto-update available models
16653
+ * TODO: [🧠] Verify pricing information is current with Deepseek's official documentation
16654
+ * Note: [💞] Ignore a discrepancy between file name and entity name
16655
+ */
16656
+
15701
16657
  /**
15702
16658
  * Execution Tools for calling Deepseek API.
15703
16659
  *
@@ -15719,18 +16675,7 @@ const createDeepseekExecutionTools = Object.assign((options) => {
15719
16675
  title: 'Deepseek',
15720
16676
  description: 'Implementation of Deepseek models',
15721
16677
  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
- ],
16678
+ availableModels: DEEPSEEK_MODELS,
15734
16679
  ...options,
15735
16680
  });
15736
16681
  }, {
@@ -15770,9 +16715,11 @@ const _GoogleMetadataRegistration = $llmToolsMetadataRegister.register({
15770
16715
  packageName: '@promptbook/google',
15771
16716
  className: 'GoogleExecutionTools',
15772
16717
  envVariables: ['GOOGLE_GENERATIVE_AI_API_KEY'],
16718
+ trustLevel: 'CLOSED',
16719
+ order: MODEL_ORDERS.NORMAL,
15773
16720
  getBoilerplateConfiguration() {
15774
16721
  return {
15775
- title: 'Google Gemini (boilerplate)',
16722
+ title: 'Google Gemini',
15776
16723
  packageName: '@promptbook/google',
15777
16724
  className: 'GoogleExecutionTools',
15778
16725
  options: {
@@ -15805,6 +16752,173 @@ const _GoogleMetadataRegistration = $llmToolsMetadataRegister.register({
15805
16752
  * Note: [💞] Ignore a discrepancy between file name and entity name
15806
16753
  */
15807
16754
 
16755
+ /**
16756
+ * List of available Google models with descriptions
16757
+ *
16758
+ * Note: Done at 2025-05-06
16759
+ *
16760
+ * @see https://ai.google.dev/models/gemini
16761
+ * @public exported from `@promptbook/google`
16762
+ */
16763
+ const GOOGLE_MODELS = exportJson({
16764
+ name: 'GOOGLE_MODELS',
16765
+ value: [
16766
+ {
16767
+ modelVariant: 'CHAT',
16768
+ modelTitle: 'Gemini 2.5 Pro',
16769
+ modelName: 'gemini-2.5-pro-preview-03-25',
16770
+ 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.',
16771
+ pricing: {
16772
+ prompt: computeUsage(`$8.00 / 1M tokens`),
16773
+ output: computeUsage(`$24.00 / 1M tokens`),
16774
+ },
16775
+ },
16776
+ {
16777
+ modelVariant: 'CHAT',
16778
+ modelTitle: 'Gemini 2.0 Flash',
16779
+ modelName: 'gemini-2.0-flash',
16780
+ 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.',
16781
+ pricing: {
16782
+ prompt: computeUsage(`$0.35 / 1M tokens`),
16783
+ output: computeUsage(`$1.05 / 1M tokens`),
16784
+ },
16785
+ },
16786
+ {
16787
+ modelVariant: 'CHAT',
16788
+ modelTitle: 'Gemini 2.0 Flash Lite',
16789
+ modelName: 'gemini-2.0-flash-lite',
16790
+ modelDescription: 'Streamlined version of Gemini 2.0 Flash, designed for extremely low-latency applications and edge deployments. Optimized for efficiency while maintaining core capabilities.',
16791
+ pricing: {
16792
+ prompt: computeUsage(`$0.20 / 1M tokens`),
16793
+ output: computeUsage(`$0.60 / 1M tokens`),
16794
+ },
16795
+ },
16796
+ {
16797
+ modelVariant: 'CHAT',
16798
+ modelTitle: 'Gemini 2.0 Flash Thinking',
16799
+ modelName: 'gemini-2.0-flash-thinking-exp-01-21',
16800
+ modelDescription: 'Experimental model focused on enhanced reasoning with explicit chain-of-thought processes. Designed for tasks requiring structured thinking and problem-solving approaches.',
16801
+ pricing: {
16802
+ prompt: computeUsage(`$0.35 / 1M tokens`),
16803
+ output: computeUsage(`$1.05 / 1M tokens`),
16804
+ },
16805
+ },
16806
+ {
16807
+ modelVariant: 'CHAT',
16808
+ modelTitle: 'Gemini 1.5 Flash',
16809
+ modelName: 'gemini-1.5-flash',
16810
+ modelDescription: 'Efficient model balancing speed and quality for general-purpose applications. 1M token context window with good multimodal capabilities and quick response times.',
16811
+ pricing: {
16812
+ prompt: computeUsage(`$0.25 / 1M tokens`),
16813
+ output: computeUsage(`$0.75 / 1M tokens`),
16814
+ },
16815
+ },
16816
+ {
16817
+ modelVariant: 'CHAT',
16818
+ modelTitle: 'Gemini 1.5 Flash Latest',
16819
+ modelName: 'gemini-1.5-flash-latest',
16820
+ 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.',
16821
+ },
16822
+ {
16823
+ modelVariant: 'CHAT',
16824
+ modelTitle: 'Gemini 1.5 Flash 001',
16825
+ modelName: 'gemini-1.5-flash-001',
16826
+ modelDescription: 'First stable release of Gemini 1.5 Flash model with reliable performance characteristics for production applications. 1M token context window.',
16827
+ },
16828
+ {
16829
+ modelVariant: 'CHAT',
16830
+ modelTitle: 'Gemini 1.5 Flash 002',
16831
+ modelName: 'gemini-1.5-flash-002',
16832
+ modelDescription: 'Improved version of Gemini 1.5 Flash with enhanced instruction following and more consistent outputs. Refined for better application integration.',
16833
+ },
16834
+ {
16835
+ modelVariant: 'CHAT',
16836
+ modelTitle: 'Gemini 1.5 Flash Exp',
16837
+ modelName: 'gemini-1.5-flash-exp-0827',
16838
+ 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.',
16839
+ },
16840
+ {
16841
+ modelVariant: 'CHAT',
16842
+ modelTitle: 'Gemini 1.5 Flash 8B',
16843
+ modelName: 'gemini-1.5-flash-8b',
16844
+ modelDescription: 'Compact 8B parameter model optimized for efficiency and deployment in resource-constrained environments. Good performance despite smaller size.',
16845
+ },
16846
+ {
16847
+ modelVariant: 'CHAT',
16848
+ modelTitle: 'Gemini 1.5 Flash 8B Latest',
16849
+ modelName: 'gemini-1.5-flash-8b-latest',
16850
+ modelDescription: 'Points to the most recent version of the compact 8B parameter model, providing latest improvements while maintaining a small footprint.',
16851
+ },
16852
+ {
16853
+ modelVariant: 'CHAT',
16854
+ modelTitle: 'Gemini 1.5 Flash 8B Exp',
16855
+ modelName: 'gemini-1.5-flash-8b-exp-0924',
16856
+ modelDescription: 'Experimental version of the 8B parameter model with new capabilities and optimizations being evaluated for future stable releases.',
16857
+ },
16858
+ {
16859
+ modelVariant: 'CHAT',
16860
+ modelTitle: 'Gemini 1.5 Flash 8B Exp',
16861
+ modelName: 'gemini-1.5-flash-8b-exp-0827',
16862
+ modelDescription: 'August experimental release of the efficient 8B parameter model with specific improvements to reasoning capabilities and response quality.',
16863
+ },
16864
+ {
16865
+ modelVariant: 'CHAT',
16866
+ modelTitle: 'Gemini 1.5 Pro Latest',
16867
+ modelName: 'gemini-1.5-pro-latest',
16868
+ modelDescription: 'Points to the most recent version of the flagship Gemini 1.5 Pro model, ensuring access to the latest capabilities and improvements.',
16869
+ pricing: {
16870
+ prompt: computeUsage(`$7.00 / 1M tokens`),
16871
+ output: computeUsage(`$21.00 / 1M tokens`),
16872
+ },
16873
+ },
16874
+ {
16875
+ modelVariant: 'CHAT',
16876
+ modelTitle: 'Gemini 1.5 Pro',
16877
+ modelName: 'gemini-1.5-pro',
16878
+ modelDescription: 'Flagship multimodal model with strong performance across text, code, vision, and audio tasks. 1M token context window with excellent reasoning capabilities.',
16879
+ pricing: {
16880
+ prompt: computeUsage(`$6.00 / 1M tokens`),
16881
+ output: computeUsage(`$18.00 / 1M tokens`),
16882
+ },
16883
+ },
16884
+ {
16885
+ modelVariant: 'CHAT',
16886
+ modelTitle: 'Gemini 1.5 Pro 001',
16887
+ modelName: 'gemini-1.5-pro-001',
16888
+ modelDescription: 'First stable release of Gemini 1.5 Pro with consistent performance characteristics and reliable behavior for production applications.',
16889
+ },
16890
+ {
16891
+ modelVariant: 'CHAT',
16892
+ modelTitle: 'Gemini 1.5 Pro 002',
16893
+ modelName: 'gemini-1.5-pro-002',
16894
+ modelDescription: 'Refined version of Gemini 1.5 Pro with improved instruction following, better multimodal understanding, and more consistent outputs.',
16895
+ },
16896
+ {
16897
+ modelVariant: 'CHAT',
16898
+ modelTitle: 'Gemini 1.5 Pro Exp',
16899
+ modelName: 'gemini-1.5-pro-exp-0827',
16900
+ modelDescription: 'Experimental version of Gemini 1.5 Pro with new capabilities and optimizations being tested before wider release. May offer improved performance.',
16901
+ },
16902
+ {
16903
+ modelVariant: 'CHAT',
16904
+ modelTitle: 'Gemini 1.0 Pro',
16905
+ modelName: 'gemini-1.0-pro',
16906
+ modelDescription: 'Original Gemini series foundation model with solid multimodal capabilities. 32K context window with good performance on text, code, and basic vision tasks.',
16907
+ pricing: {
16908
+ prompt: computeUsage(`$0.35 / 1M tokens`),
16909
+ output: computeUsage(`$1.05 / 1M tokens`),
16910
+ },
16911
+ },
16912
+ // <- [🕕]
16913
+ ],
16914
+ });
16915
+ /**
16916
+ * TODO: [🧠] Add information about context window sizes, capabilities, and relative performance characteristics
16917
+ * TODO: [🎰] Some mechanism to auto-update available models
16918
+ * TODO: [🧠] Verify pricing information is current with Google's official documentation
16919
+ * Note: [💞] Ignore a discrepancy between file name and entity name
16920
+ */
16921
+
15808
16922
  /**
15809
16923
  * Execution Tools for calling Google Gemini API.
15810
16924
  *
@@ -15826,29 +16940,7 @@ const createGoogleExecutionTools = Object.assign((options) => {
15826
16940
  title: 'Google',
15827
16941
  description: 'Implementation of Google models',
15828
16942
  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' })),
16943
+ availableModels: GOOGLE_MODELS,
15852
16944
  ...options,
15853
16945
  });
15854
16946
  }, {
@@ -15888,13 +16980,16 @@ const _OpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
15888
16980
  packageName: '@promptbook/openai',
15889
16981
  className: 'OpenAiExecutionTools',
15890
16982
  envVariables: ['OPENAI_API_KEY'],
16983
+ trustLevel: 'CLOSED',
16984
+ order: MODEL_ORDERS.TOP_TIER,
15891
16985
  getBoilerplateConfiguration() {
15892
16986
  return {
15893
- title: 'Open AI (boilerplate)',
16987
+ title: 'Open AI',
15894
16988
  packageName: '@promptbook/openai',
15895
16989
  className: 'OpenAiExecutionTools',
15896
16990
  options: {
15897
16991
  apiKey: 'sk-',
16992
+ maxRequestsPerMinute: DEFAULT_MAX_REQUESTS_PER_MINUTE,
15898
16993
  },
15899
16994
  };
15900
16995
  },
@@ -15914,9 +17009,9 @@ const _OpenAiMetadataRegistration = $llmToolsMetadataRegister.register({
15914
17009
  },
15915
17010
  });
15916
17011
  /**
15917
- * @@@ registration1 of default configuration for Open AI
17012
+ * Registration of the OpenAI Assistant metadata
15918
17013
  *
15919
- * Note: [🏐] Configurations registrations are done in @@@ BUT constructor @@@
17014
+ * Note: [🏐] Configurations registrations are done in the metadata registration section, but the constructor registration is handled separately.
15920
17015
  *
15921
17016
  * @public exported from `@promptbook/core`
15922
17017
  * @public exported from `@promptbook/wizzard`
@@ -15928,9 +17023,11 @@ const _OpenAiAssistantMetadataRegistration = $llmToolsMetadataRegister.register(
15928
17023
  className: 'OpenAiAssistantExecutionTools',
15929
17024
  envVariables: null,
15930
17025
  // <- TODO: ['OPENAI_API_KEY', 'OPENAI_ASSISTANT_ID']
17026
+ trustLevel: 'CLOSED',
17027
+ order: MODEL_ORDERS.NORMAL,
15931
17028
  getBoilerplateConfiguration() {
15932
17029
  return {
15933
- title: 'Open AI Assistant (boilerplate)',
17030
+ title: 'Open AI Assistant',
15934
17031
  packageName: '@promptbook/openai',
15935
17032
  className: 'OpenAiAssistantExecutionTools',
15936
17033
  options: {
@@ -15975,20 +17072,39 @@ function computeOpenAiUsage(promptContent, // <- Note: Intentionally using [] to
15975
17072
  resultContent, rawResponse) {
15976
17073
  var _a, _b;
15977
17074
  if (rawResponse.usage === undefined) {
17075
+ console.log('!!! computeOpenAiUsage', 'The usage is not defined in the response from OpenAI');
15978
17076
  throw new PipelineExecutionError('The usage is not defined in the response from OpenAI');
15979
17077
  }
15980
17078
  if (((_a = rawResponse.usage) === null || _a === void 0 ? void 0 : _a.prompt_tokens) === undefined) {
17079
+ console.log('!!! computeOpenAiUsage', 'In OpenAI response `usage.prompt_tokens` not defined');
15981
17080
  throw new PipelineExecutionError('In OpenAI response `usage.prompt_tokens` not defined');
15982
17081
  }
15983
17082
  const inputTokens = rawResponse.usage.prompt_tokens;
15984
17083
  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);
17084
+ let isUncertain = false;
17085
+ let modelInfo = OPENAI_MODELS.find((model) => model.modelName === rawResponse.model);
17086
+ if (modelInfo === undefined) {
17087
+ // Note: Model is not in the list of known models, fallback to the family of the models and mark price as uncertain
17088
+ modelInfo = OPENAI_MODELS.find((model) => (rawResponse.model || SALT_NONCE).startsWith(model.modelName));
17089
+ if (modelInfo !== undefined) {
17090
+ isUncertain = true;
17091
+ }
17092
+ }
17093
+ console.log('!!! computeOpenAiUsage', {
17094
+ inputTokens,
17095
+ outputTokens,
17096
+ rawResponse,
17097
+ 'rawResponse.model': rawResponse.model,
17098
+ OPENAI_MODELS,
17099
+ resultContent,
17100
+ modelInfo,
17101
+ });
15986
17102
  let price;
15987
17103
  if (modelInfo === undefined || modelInfo.pricing === undefined) {
15988
17104
  price = uncertainNumber();
15989
17105
  }
15990
17106
  else {
15991
- price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output);
17107
+ price = uncertainNumber(inputTokens * modelInfo.pricing.prompt + outputTokens * modelInfo.pricing.output, isUncertain);
15992
17108
  }
15993
17109
  return {
15994
17110
  price,
@@ -16023,6 +17139,10 @@ class OpenAiExecutionTools {
16023
17139
  * OpenAI API client.
16024
17140
  */
16025
17141
  this.client = null;
17142
+ // TODO: Allow configuring rate limits via options
17143
+ this.limiter = new Bottleneck({
17144
+ minTime: 60000 / (this.options.maxRequestsPerMinute || DEFAULT_MAX_REQUESTS_PER_MINUTE),
17145
+ });
16026
17146
  }
16027
17147
  get title() {
16028
17148
  return 'OpenAI';
@@ -16126,7 +17246,9 @@ class OpenAiExecutionTools {
16126
17246
  if (this.options.isVerbose) {
16127
17247
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
16128
17248
  }
16129
- const rawResponse = await client.chat.completions.create(rawRequest).catch((error) => {
17249
+ const rawResponse = await this.limiter
17250
+ .schedule(() => client.chat.completions.create(rawRequest))
17251
+ .catch((error) => {
16130
17252
  assertsError(error);
16131
17253
  if (this.options.isVerbose) {
16132
17254
  console.info(colors.bgRed('error'), error);
@@ -16203,7 +17325,9 @@ class OpenAiExecutionTools {
16203
17325
  if (this.options.isVerbose) {
16204
17326
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
16205
17327
  }
16206
- const rawResponse = await client.completions.create(rawRequest).catch((error) => {
17328
+ const rawResponse = await this.limiter
17329
+ .schedule(() => client.completions.create(rawRequest))
17330
+ .catch((error) => {
16207
17331
  assertsError(error);
16208
17332
  if (this.options.isVerbose) {
16209
17333
  console.info(colors.bgRed('error'), error);
@@ -16267,7 +17391,9 @@ class OpenAiExecutionTools {
16267
17391
  if (this.options.isVerbose) {
16268
17392
  console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
16269
17393
  }
16270
- const rawResponse = await client.embeddings.create(rawRequest).catch((error) => {
17394
+ const rawResponse = await this.limiter
17395
+ .schedule(() => client.embeddings.create(rawRequest))
17396
+ .catch((error) => {
16271
17397
  assertsError(error);
16272
17398
  if (this.options.isVerbose) {
16273
17399
  console.info(colors.bgRed('error'), error);
@@ -16365,6 +17491,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
16365
17491
  constructor(options) {
16366
17492
  super(options);
16367
17493
  this.assistantId = options.assistantId;
17494
+ // TODO: [👱] Make limiter same as in `OpenAiExecutionTools`
16368
17495
  }
16369
17496
  get title() {
16370
17497
  return 'OpenAI Assistant';
@@ -16566,9 +17693,9 @@ const createOpenAiExecutionTools = Object.assign((options) => {
16566
17693
  */
16567
17694
  const _OpenAiRegistration = $llmToolsRegister.register(createOpenAiExecutionTools);
16568
17695
  /**
16569
- * @@@ registration2
17696
+ * Registration of the OpenAI Assistant provider
16570
17697
  *
16571
- * Note: [🏐] Configurations registrations are done in @@@ BUT constructor @@@
17698
+ * Note: [🏐] Configurations registrations are done in register-constructor.ts BUT constructor register-constructor.ts
16572
17699
  *
16573
17700
  * @public exported from `@promptbook/openai`
16574
17701
  * @public exported from `@promptbook/wizzard`
@@ -16581,9 +17708,8 @@ const _OpenAiAssistantRegistration = $llmToolsRegister.register(createOpenAiAssi
16581
17708
  */
16582
17709
 
16583
17710
  /**
16584
- * Create a filename for intermediate cache for scrapers
16585
- *
16586
- * Note: It also checks if directory exists and creates it if not
17711
+ * Retrieves an intermediate source for a scraper based on the knowledge source.
17712
+ * Manages the caching and retrieval of intermediate scraper results for optimized performance.
16587
17713
  *
16588
17714
  * @private as internal utility for scrapers
16589
17715
  */
@@ -16810,14 +17936,14 @@ const boilerplateScraperMetadata = $deepFreeze({
16810
17936
  packageName: '@promptbook/boilerplate',
16811
17937
  className: 'BoilerplateScraper',
16812
17938
  mimeTypes: [
16813
- '@@@/@@@',
16814
- // <- TODO: @@@ Add compatible mime types with Boilerplate scraper
17939
+ '@@/@@',
17940
+ // <- TODO: @@ Add compatible mime types with Boilerplate scraper
16815
17941
  ],
16816
- documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@@',
17942
+ documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
16817
17943
  isAvilableInBrowser: false,
16818
17944
  // <- Note: [🌏] Only `MarkdownScraper` makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
16819
17945
  requiredExecutables: [
16820
- /* @@@ 'Pandoc' */
17946
+ /* @@ 'Pandoc' */
16821
17947
  ],
16822
17948
  }); /* <- Note: [🤛] */
16823
17949
  /**
@@ -16835,7 +17961,7 @@ const _BoilerplateScraperMetadataRegistration = $scrapersMetadataRegister.regist
16835
17961
  */
16836
17962
 
16837
17963
  /**
16838
- * Scraper of @@@ files
17964
+ * Scraper of @@ files
16839
17965
  *
16840
17966
  * @see `documentationUrl` for more details
16841
17967
  * @public exported from `@promptbook/boilerplate`
@@ -16853,30 +17979,30 @@ class BoilerplateScraper {
16853
17979
  this.markdownScraper = new MarkdownScraper(tools, options);
16854
17980
  }
16855
17981
  /**
16856
- * Convert the `.@@@` to `.md` file and returns intermediate source
17982
+ * Convert the `.@@` to `.md` file and returns intermediate source
16857
17983
  *
16858
17984
  * 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
17985
  */
16860
17986
  async $convert(source) {
16861
17987
  var _a;
16862
17988
  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
17989
+ // TODO: @@ Preserve or delete
16864
17990
  if (!$isRunningInNode()) {
16865
17991
  throw new KnowledgeScrapeError('BoilerplateScraper is only supported in Node environment');
16866
17992
  }
16867
- // TODO: @@@ Preserve or delete
17993
+ // TODO: @@ Preserve or delete
16868
17994
  if (this.tools.fs === undefined) {
16869
17995
  throw new EnvironmentMismatchError('Can not scrape boilerplates without filesystem tools');
16870
17996
  // <- TODO: [🧠] What is the best error type here`
16871
17997
  }
16872
- // TODO: @@@ Preserve, delete or modify
17998
+ // TODO: @@ Preserve, delete or modify
16873
17999
  if (((_a = this.tools.executables) === null || _a === void 0 ? void 0 : _a.pandocPath) === undefined) {
16874
18000
  throw new MissingToolsError('Pandoc is required for scraping .docx files');
16875
18001
  }
16876
- // TODO: @@@ Preserve, delete or modify
18002
+ // TODO: @@ Preserve, delete or modify
16877
18003
  if (source.filename === null) {
16878
18004
  // TODO: [🧠] Maybe save file as temporary
16879
- throw new KnowledgeScrapeError('When parsing .@@@ file, it must be real file in the file system');
18005
+ throw new KnowledgeScrapeError('When parsing .@@ file, it must be real file in the file system');
16880
18006
  }
16881
18007
  const extension = getFileExtension(source.filename);
16882
18008
  const cacheFilehandler = await getScraperIntermediateSource(source, {
@@ -16886,7 +18012,7 @@ class BoilerplateScraper {
16886
18012
  extension: 'md',
16887
18013
  isVerbose,
16888
18014
  });
16889
- // TODO: @@@ Preserve, delete or modify
18015
+ // TODO: @@ Preserve, delete or modify
16890
18016
  // Note: Running Pandoc ONLY if the file in the cache does not exist
16891
18017
  if (!(await isFileExisting(cacheFilehandler.filename, this.tools.fs))) {
16892
18018
  const command = `"${this.tools.executables.pandocPath}" -f ${extension} -t markdown "${source.filename}" -o "${cacheFilehandler.filename}"`;
@@ -16912,7 +18038,7 @@ class BoilerplateScraper {
16912
18038
  */
16913
18039
  async scrape(source) {
16914
18040
  const cacheFilehandler = await this.$convert(source);
16915
- // TODO: @@@ Preserve, delete or modify
18041
+ // TODO: @@ Preserve, delete or modify
16916
18042
  const markdownSource = {
16917
18043
  source: source.source,
16918
18044
  filename: cacheFilehandler.filename,
@@ -16943,7 +18069,7 @@ class BoilerplateScraper {
16943
18069
  * TODO: [👣] Converted documents can act as cached items - there is no need to run conversion each time
16944
18070
  * TODO: [🪂] Do it in parallel
16945
18071
  * 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
18072
+ * @@ Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
16947
18073
  */
16948
18074
 
16949
18075
  /**
@@ -17277,7 +18403,8 @@ class LegacyDocumentScraper {
17277
18403
  */
17278
18404
 
17279
18405
  /**
17280
- * @@@
18406
+ * Creates a scraper for legacy document formats (.doc, .rtf, etc).
18407
+ * Uses LibreOffice for conversion to extract content from older document formats.
17281
18408
  *
17282
18409
  * @public exported from `@promptbook/legacy-documents`
17283
18410
  */
@@ -17304,7 +18431,7 @@ const _LegacyDocumentScraperRegistration = $scrapersRegister.register(createLega
17304
18431
  */
17305
18432
 
17306
18433
  /**
17307
- * @@@
18434
+ * Creates a scraper for document content.
17308
18435
  *
17309
18436
  * @public exported from `@promptbook/documents`
17310
18437
  */
@@ -17331,7 +18458,7 @@ const _DocumentScraperRegistration = $scrapersRegister.register(createDocumentSc
17331
18458
  */
17332
18459
 
17333
18460
  /**
17334
- * @@@
18461
+ * Creates a scraper for markdown content.
17335
18462
  *
17336
18463
  * @public exported from `@promptbook/markdown-utils`
17337
18464
  */
@@ -17437,8 +18564,8 @@ class MarkitdownScraper {
17437
18564
  extension: 'md',
17438
18565
  isVerbose,
17439
18566
  });
17440
- // TODO: @@@ Preserve, delete or modify
17441
- // Note: Running Pandoc ONLY if the file in the cache does not exist
18567
+ // TODO: Determine if Markitdown conversion should run only if the cache file doesn't exist, or always.
18568
+ // Note: Running Markitdown conversion ONLY if the file in the cache does not exist
17442
18569
  if (!(await isFileExisting(cacheFilehandler.filename, this.tools.fs))) {
17443
18570
  const src = source.filename || source.url || null;
17444
18571
  // console.log('!!', { src, source, cacheFilehandler });
@@ -17460,11 +18587,11 @@ class MarkitdownScraper {
17460
18587
  return cacheFilehandler;
17461
18588
  }
17462
18589
  /**
17463
- * Scrapes the docx file and returns the knowledge pieces or `null` if it can't scrape it
18590
+ * Scrapes the source document (PDF, DOCX, etc.) and returns the knowledge pieces or `null` if it can't scrape it.
17464
18591
  */
17465
18592
  async scrape(source) {
17466
18593
  const cacheFilehandler = await this.$convert(source);
17467
- // TODO: @@@ Preserve, delete or modify
18594
+ // TODO: Ensure this correctly creates the source object for the internal MarkdownScraper using the converted file.
17468
18595
  const markdownSource = {
17469
18596
  source: source.source,
17470
18597
  filename: cacheFilehandler.filename,
@@ -17608,7 +18735,8 @@ class PdfScraper {
17608
18735
  */
17609
18736
 
17610
18737
  /**
17611
- * @@@
18738
+ * Factory function to create an instance of PdfScraper.
18739
+ * It bundles the scraper class with its metadata.
17612
18740
  *
17613
18741
  * @public exported from `@promptbook/pdf`
17614
18742
  */
@@ -17784,7 +18912,8 @@ class WebsiteScraper {
17784
18912
  */
17785
18913
 
17786
18914
  /**
17787
- * @@@
18915
+ * Factory function to create an instance of WebsiteScraper.
18916
+ * It bundles the scraper class with its metadata.
17788
18917
  *
17789
18918
  * @public exported from `@promptbook/website-crawler`
17790
18919
  */