@promptbook/node 0.98.0-5 โ†’ 0.98.0-8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/umd/index.umd.js CHANGED
@@ -46,7 +46,7 @@
46
46
  * @generated
47
47
  * @see https://github.com/webgptorg/promptbook
48
48
  */
49
- const PROMPTBOOK_ENGINE_VERSION = '0.98.0-5';
49
+ const PROMPTBOOK_ENGINE_VERSION = '0.98.0-8';
50
50
  /**
51
51
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
52
52
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -190,7 +190,7 @@
190
190
  *
191
191
  * @public exported from `@promptbook/core`
192
192
  */
193
- const DEFAULT_MAX_EXECUTION_ATTEMPTS = 3; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
193
+ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
194
194
  // <- TODO: [๐Ÿ•] Make also `BOOKS_DIRNAME_ALTERNATIVES`
195
195
  // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
196
196
  /**
@@ -360,7 +360,7 @@
360
360
  throw new Error(spaceTrim__default["default"]((block) => `
361
361
  ${block(error.message)}
362
362
 
363
- The JSON text:
363
+ The expected JSON text:
364
364
  ${block(value)}
365
365
  `));
366
366
  }
@@ -3121,108 +3121,6 @@
3121
3121
  * TODO: [๐Ÿ‘ทโ€โ™‚๏ธ] @@@ Manual about construction of llmTools
3122
3122
  */
3123
3123
 
3124
- /**
3125
- * Extracts all code blocks from markdown.
3126
- *
3127
- * Note: There are multiple similar functions:
3128
- * - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
3129
- * - `extractJsonBlock` extracts exactly one valid JSON code block
3130
- * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
3131
- * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
3132
- *
3133
- * @param markdown any valid markdown
3134
- * @returns code blocks with language and content
3135
- * @throws {ParseError} if block is not closed properly
3136
- * @public exported from `@promptbook/markdown-utils`
3137
- */
3138
- function extractAllBlocksFromMarkdown(markdown) {
3139
- const codeBlocks = [];
3140
- const lines = markdown.split('\n');
3141
- // Note: [0] Ensure that the last block notated by gt > will be closed
3142
- lines.push('');
3143
- let currentCodeBlock = null;
3144
- for (const line of lines) {
3145
- if (line.startsWith('> ') || line === '>') {
3146
- if (currentCodeBlock === null) {
3147
- currentCodeBlock = { blockNotation: '>', language: null, content: '' };
3148
- } /* not else */
3149
- if (currentCodeBlock.blockNotation === '>') {
3150
- if (currentCodeBlock.content !== '') {
3151
- currentCodeBlock.content += '\n';
3152
- }
3153
- currentCodeBlock.content += line.slice(2);
3154
- }
3155
- }
3156
- else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '>' /* <- Note: [0] */) {
3157
- codeBlocks.push(currentCodeBlock);
3158
- currentCodeBlock = null;
3159
- }
3160
- /* not else */
3161
- if (line.startsWith('```')) {
3162
- const language = line.slice(3).trim() || null;
3163
- if (currentCodeBlock === null) {
3164
- currentCodeBlock = { blockNotation: '```', language, content: '' };
3165
- }
3166
- else {
3167
- if (language !== null) {
3168
- throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed and already opening new ${language} code block`);
3169
- }
3170
- codeBlocks.push(currentCodeBlock);
3171
- currentCodeBlock = null;
3172
- }
3173
- }
3174
- else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '```') {
3175
- if (currentCodeBlock.content !== '') {
3176
- currentCodeBlock.content += '\n';
3177
- }
3178
- currentCodeBlock.content += line.split('\\`\\`\\`').join('```') /* <- TODO: Maybe make proper unescape */;
3179
- }
3180
- }
3181
- if (currentCodeBlock !== null) {
3182
- throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed at the end of the markdown`);
3183
- }
3184
- return codeBlocks;
3185
- }
3186
- /**
3187
- * TODO: Maybe name for `blockNotation` instead of '```' and '>'
3188
- */
3189
-
3190
- /**
3191
- * Extracts extracts exactly one valid JSON code block
3192
- *
3193
- * - When given string is a valid JSON as it is, it just returns it
3194
- * - When there is no JSON code block the function throws a `ParseError`
3195
- * - When there are multiple JSON code blocks the function throws a `ParseError`
3196
- *
3197
- * Note: It is not important if marked as ```json BUT if it is VALID JSON
3198
- * Note: There are multiple similar function:
3199
- * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
3200
- * - `extractJsonBlock` extracts exactly one valid JSON code block
3201
- * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
3202
- * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
3203
- *
3204
- * @public exported from `@promptbook/markdown-utils`
3205
- * @throws {ParseError} if there is no valid JSON block in the markdown
3206
- */
3207
- function extractJsonBlock(markdown) {
3208
- if (isValidJsonString(markdown)) {
3209
- return markdown;
3210
- }
3211
- const codeBlocks = extractAllBlocksFromMarkdown(markdown);
3212
- const jsonBlocks = codeBlocks.filter(({ content }) => isValidJsonString(content));
3213
- if (jsonBlocks.length === 0) {
3214
- throw new Error('There is no valid JSON block in the markdown');
3215
- }
3216
- if (jsonBlocks.length > 1) {
3217
- throw new Error('There are multiple JSON code blocks in the markdown');
3218
- }
3219
- return jsonBlocks[0].content;
3220
- }
3221
- /**
3222
- * TODO: Add some auto-healing logic + extract YAML, JSON5, TOML, etc.
3223
- * TODO: [๐Ÿข] Make this logic part of `JsonFormatParser` or `isValidJsonString`
3224
- */
3225
-
3226
3124
  /**
3227
3125
  * Takes an item or an array of items and returns an array of items
3228
3126
  *
@@ -3330,6 +3228,108 @@
3330
3228
  return replacedTemplates;
3331
3229
  }
3332
3230
 
3231
+ /**
3232
+ * Extracts all code blocks from markdown.
3233
+ *
3234
+ * Note: There are multiple similar functions:
3235
+ * - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
3236
+ * - `extractJsonBlock` extracts exactly one valid JSON code block
3237
+ * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
3238
+ * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
3239
+ *
3240
+ * @param markdown any valid markdown
3241
+ * @returns code blocks with language and content
3242
+ * @throws {ParseError} if block is not closed properly
3243
+ * @public exported from `@promptbook/markdown-utils`
3244
+ */
3245
+ function extractAllBlocksFromMarkdown(markdown) {
3246
+ const codeBlocks = [];
3247
+ const lines = markdown.split('\n');
3248
+ // Note: [0] Ensure that the last block notated by gt > will be closed
3249
+ lines.push('');
3250
+ let currentCodeBlock = null;
3251
+ for (const line of lines) {
3252
+ if (line.startsWith('> ') || line === '>') {
3253
+ if (currentCodeBlock === null) {
3254
+ currentCodeBlock = { blockNotation: '>', language: null, content: '' };
3255
+ } /* not else */
3256
+ if (currentCodeBlock.blockNotation === '>') {
3257
+ if (currentCodeBlock.content !== '') {
3258
+ currentCodeBlock.content += '\n';
3259
+ }
3260
+ currentCodeBlock.content += line.slice(2);
3261
+ }
3262
+ }
3263
+ else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '>' /* <- Note: [0] */) {
3264
+ codeBlocks.push(currentCodeBlock);
3265
+ currentCodeBlock = null;
3266
+ }
3267
+ /* not else */
3268
+ if (line.startsWith('```')) {
3269
+ const language = line.slice(3).trim() || null;
3270
+ if (currentCodeBlock === null) {
3271
+ currentCodeBlock = { blockNotation: '```', language, content: '' };
3272
+ }
3273
+ else {
3274
+ if (language !== null) {
3275
+ throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed and already opening new ${language} code block`);
3276
+ }
3277
+ codeBlocks.push(currentCodeBlock);
3278
+ currentCodeBlock = null;
3279
+ }
3280
+ }
3281
+ else if (currentCodeBlock !== null && currentCodeBlock.blockNotation === '```') {
3282
+ if (currentCodeBlock.content !== '') {
3283
+ currentCodeBlock.content += '\n';
3284
+ }
3285
+ currentCodeBlock.content += line.split('\\`\\`\\`').join('```') /* <- TODO: Maybe make proper unescape */;
3286
+ }
3287
+ }
3288
+ if (currentCodeBlock !== null) {
3289
+ throw new ParseError(`${capitalize(currentCodeBlock.language || 'the')} code block was not closed at the end of the markdown`);
3290
+ }
3291
+ return codeBlocks;
3292
+ }
3293
+ /**
3294
+ * TODO: Maybe name for `blockNotation` instead of '```' and '>'
3295
+ */
3296
+
3297
+ /**
3298
+ * Extracts extracts exactly one valid JSON code block
3299
+ *
3300
+ * - When given string is a valid JSON as it is, it just returns it
3301
+ * - When there is no JSON code block the function throws a `ParseError`
3302
+ * - When there are multiple JSON code blocks the function throws a `ParseError`
3303
+ *
3304
+ * Note: It is not important if marked as ```json BUT if it is VALID JSON
3305
+ * Note: There are multiple similar function:
3306
+ * - `extractBlock` just extracts the content of the code block which is also used as build-in function for postprocessing
3307
+ * - `extractJsonBlock` extracts exactly one valid JSON code block
3308
+ * - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
3309
+ * - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
3310
+ *
3311
+ * @public exported from `@promptbook/markdown-utils`
3312
+ * @throws {ParseError} if there is no valid JSON block in the markdown
3313
+ */
3314
+ function extractJsonBlock(markdown) {
3315
+ if (isValidJsonString(markdown)) {
3316
+ return markdown;
3317
+ }
3318
+ const codeBlocks = extractAllBlocksFromMarkdown(markdown);
3319
+ const jsonBlocks = codeBlocks.filter(({ content }) => isValidJsonString(content));
3320
+ if (jsonBlocks.length === 0) {
3321
+ throw new Error('There is no valid JSON block in the markdown');
3322
+ }
3323
+ if (jsonBlocks.length > 1) {
3324
+ throw new Error('There are multiple JSON code blocks in the markdown');
3325
+ }
3326
+ return jsonBlocks[0].content;
3327
+ }
3328
+ /**
3329
+ * TODO: Add some auto-healing logic + extract YAML, JSON5, TOML, etc.
3330
+ * TODO: [๐Ÿข] Make this logic part of `JsonFormatParser` or `isValidJsonString`
3331
+ */
3332
+
3333
3333
  /**
3334
3334
  * Counts number of characters in the text
3335
3335
  *
@@ -3751,6 +3751,68 @@
3751
3751
  * Note: [๐Ÿ’] and [๐Ÿค ] are interconnected together
3752
3752
  */
3753
3753
 
3754
+ /**
3755
+ * Validates a prompt result against expectations and format requirements.
3756
+ * This function provides a common abstraction for result validation that can be used
3757
+ * by both execution logic and caching logic to ensure consistency.
3758
+ *
3759
+ * @param options - The validation options including result string, expectations, and format
3760
+ * @returns Validation result with processed string and validity status
3761
+ * @private internal function of `createPipelineExecutor` and `cacheLlmTools`
3762
+ */
3763
+ function validatePromptResult(options) {
3764
+ const { resultString, expectations, format } = options;
3765
+ let processedResultString = resultString;
3766
+ let validationError;
3767
+ try {
3768
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
3769
+ if (format) {
3770
+ if (format === 'JSON') {
3771
+ if (!isValidJsonString(processedResultString)) {
3772
+ // TODO: [๐Ÿข] Do more universally via `FormatParser`
3773
+ try {
3774
+ processedResultString = extractJsonBlock(processedResultString);
3775
+ }
3776
+ catch (error) {
3777
+ keepUnused(error);
3778
+ throw new ExpectError(spaceTrim.spaceTrim((block) => `
3779
+ Expected valid JSON string
3780
+
3781
+ The expected JSON text:
3782
+ ${block(processedResultString)}
3783
+ `));
3784
+ }
3785
+ }
3786
+ }
3787
+ else {
3788
+ throw new UnexpectedError(`Unknown format "${format}"`);
3789
+ }
3790
+ }
3791
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
3792
+ if (expectations) {
3793
+ checkExpectations(expectations, processedResultString);
3794
+ }
3795
+ return {
3796
+ isValid: true,
3797
+ processedResultString,
3798
+ };
3799
+ }
3800
+ catch (error) {
3801
+ if (error instanceof ExpectError) {
3802
+ validationError = error;
3803
+ }
3804
+ else {
3805
+ // Re-throw non-ExpectError errors (like UnexpectedError)
3806
+ throw error;
3807
+ }
3808
+ return {
3809
+ isValid: false,
3810
+ processedResultString,
3811
+ error: validationError,
3812
+ };
3813
+ }
3814
+ }
3815
+
3754
3816
  /**
3755
3817
  * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
3756
3818
  * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
@@ -3773,13 +3835,13 @@
3773
3835
  // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
3774
3836
  const _llms = arrayableToArray(tools.llm);
3775
3837
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
3776
- attempts: for (let attempt = -jokerParameterNames.length; attempt < maxAttempts; attempt++) {
3777
- const isJokerAttempt = attempt < 0;
3778
- const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
3838
+ attempts: for (let attemptIndex = -jokerParameterNames.length; attemptIndex < maxAttempts; attemptIndex++) {
3839
+ const isJokerAttempt = attemptIndex < 0;
3840
+ const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attemptIndex];
3779
3841
  // TODO: [๐Ÿง ][๐Ÿญ] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
3780
3842
  if (isJokerAttempt && !jokerParameterName) {
3781
3843
  throw new UnexpectedError(spaceTrim.spaceTrim((block) => `
3782
- Joker not found in attempt ${attempt}
3844
+ Joker not found in attempt ${attemptIndex}
3783
3845
 
3784
3846
  ${block(pipelineIdentification)}
3785
3847
  `));
@@ -3977,35 +4039,18 @@
3977
4039
  }
3978
4040
  }
3979
4041
  // TODO: [๐Ÿ’] Unite object for expecting amount and format
3980
- if (task.format) {
3981
- if (task.format === 'JSON') {
3982
- if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
3983
- // TODO: [๐Ÿข] Do more universally via `FormatParser`
3984
- try {
3985
- $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
3986
- }
3987
- catch (error) {
3988
- keepUnused(error);
3989
- throw new ExpectError(spaceTrim.spaceTrim((block) => `
3990
- Expected valid JSON string
3991
-
3992
- ${block(
3993
- /*<- Note: No need for `pipelineIdentification`, it will be catched and added later */ '')}
3994
- `));
3995
- }
3996
- }
3997
- }
3998
- else {
3999
- throw new UnexpectedError(spaceTrim.spaceTrim((block) => `
4000
- Unknown format "${task.format}"
4001
-
4002
- ${block(pipelineIdentification)}
4003
- `));
4042
+ // Use the common validation function for both format and expectations
4043
+ if (task.format || task.expectations) {
4044
+ const validationResult = validatePromptResult({
4045
+ resultString: $ongoingTaskResult.$resultString || '',
4046
+ expectations: task.expectations,
4047
+ format: task.format,
4048
+ });
4049
+ if (!validationResult.isValid) {
4050
+ throw validationResult.error;
4004
4051
  }
4005
- }
4006
- // TODO: [๐Ÿ’] Unite object for expecting amount and format
4007
- if (task.expectations) {
4008
- checkExpectations(task.expectations, $ongoingTaskResult.$resultString || '');
4052
+ // Update the result string in case format processing modified it (e.g., JSON extraction)
4053
+ $ongoingTaskResult.$resultString = validationResult.processedResultString;
4009
4054
  }
4010
4055
  break attempts;
4011
4056
  }
@@ -4019,6 +4064,7 @@
4019
4064
  $ongoingTaskResult.$failedResults = [];
4020
4065
  }
4021
4066
  $ongoingTaskResult.$failedResults.push({
4067
+ attemptIndex,
4022
4068
  result: $ongoingTaskResult.$resultString,
4023
4069
  error: error,
4024
4070
  });
@@ -4043,19 +4089,13 @@
4043
4089
  });
4044
4090
  }
4045
4091
  }
4046
- if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
4047
- // Store the current failure before throwing
4048
- $ongoingTaskResult.$failedResults = $ongoingTaskResult.$failedResults || [];
4049
- $ongoingTaskResult.$failedResults.push({
4050
- result: $ongoingTaskResult.$resultString,
4051
- error: $ongoingTaskResult.$expectError,
4052
- });
4053
- // Create a summary of all failures
4092
+ if ($ongoingTaskResult.$expectError !== null && attemptIndex === maxAttempts - 1) {
4093
+ // Note: Create a summary of all failures
4054
4094
  const failuresSummary = $ongoingTaskResult.$failedResults
4055
- .map((failure, index) => spaceTrim.spaceTrim((block) => {
4095
+ .map((failure) => spaceTrim.spaceTrim((block) => {
4056
4096
  var _a, _b;
4057
4097
  return `
4058
- Attempt ${index + 1}:
4098
+ Attempt ${failure.attemptIndex + 1}:
4059
4099
  Error ${((_a = failure.error) === null || _a === void 0 ? void 0 : _a.name) || ''}:
4060
4100
  ${block((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message.split('\n').map((line) => `> ${line}`).join('\n'))}
4061
4101