@promptbook/legacy-documents 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
@@ -26,7 +26,7 @@
26
26
  * @generated
27
27
  * @see https://github.com/webgptorg/promptbook
28
28
  */
29
- const PROMPTBOOK_ENGINE_VERSION = '0.98.0-5';
29
+ const PROMPTBOOK_ENGINE_VERSION = '0.98.0-8';
30
30
  /**
31
31
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
32
32
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -175,7 +175,7 @@
175
175
  *
176
176
  * @public exported from `@promptbook/core`
177
177
  */
178
- const DEFAULT_MAX_EXECUTION_ATTEMPTS = 3; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
178
+ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
179
179
  // <- TODO: [๐Ÿ•] Make also `BOOKS_DIRNAME_ALTERNATIVES`
180
180
  // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
181
181
  /**
@@ -2560,7 +2560,7 @@
2560
2560
  throw new Error(spaceTrim__default["default"]((block) => `
2561
2561
  ${block(error.message)}
2562
2562
 
2563
- The JSON text:
2563
+ The expected JSON text:
2564
2564
  ${block(value)}
2565
2565
  `));
2566
2566
  }
@@ -4637,6 +4637,77 @@
4637
4637
  return mappedParameters;
4638
4638
  }
4639
4639
 
4640
+ /**
4641
+ * Replaces parameters in template with values from parameters object
4642
+ *
4643
+ * Note: This function is not places strings into string,
4644
+ * It's more complex and can handle this operation specifically for LLM models
4645
+ *
4646
+ * @param template the template with parameters in {curly} braces
4647
+ * @param parameters the object with parameters
4648
+ * @returns the template with replaced parameters
4649
+ * @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
4650
+ * @public exported from `@promptbook/utils`
4651
+ */
4652
+ function templateParameters(template, parameters) {
4653
+ for (const [parameterName, parameterValue] of Object.entries(parameters)) {
4654
+ if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
4655
+ throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
4656
+ }
4657
+ else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
4658
+ // TODO: [๐Ÿต]
4659
+ throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
4660
+ }
4661
+ }
4662
+ let replacedTemplates = template;
4663
+ let match;
4664
+ let loopLimit = LOOP_LIMIT;
4665
+ while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
4666
+ .exec(replacedTemplates))) {
4667
+ if (loopLimit-- < 0) {
4668
+ throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
4669
+ }
4670
+ const precol = match.groups.precol;
4671
+ const parameterName = match.groups.parameterName;
4672
+ if (parameterName === '') {
4673
+ // Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
4674
+ continue;
4675
+ }
4676
+ if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
4677
+ throw new PipelineExecutionError('Parameter is already opened or not closed');
4678
+ }
4679
+ if (parameters[parameterName] === undefined) {
4680
+ throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4681
+ }
4682
+ let parameterValue = parameters[parameterName];
4683
+ if (parameterValue === undefined) {
4684
+ throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4685
+ }
4686
+ parameterValue = valueToString(parameterValue);
4687
+ // Escape curly braces in parameter values to prevent prompt-injection
4688
+ parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4689
+ if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
4690
+ parameterValue = parameterValue
4691
+ .split('\n')
4692
+ .map((line, index) => (index === 0 ? line : `${precol}${line}`))
4693
+ .join('\n');
4694
+ }
4695
+ replacedTemplates =
4696
+ replacedTemplates.substring(0, match.index + precol.length) +
4697
+ parameterValue +
4698
+ replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
4699
+ }
4700
+ // [๐Ÿ’ซ] Check if there are parameters that are not closed properly
4701
+ if (/{\w+$/.test(replacedTemplates)) {
4702
+ throw new PipelineExecutionError('Parameter is not closed');
4703
+ }
4704
+ // [๐Ÿ’ซ] Check if there are parameters that are not opened properly
4705
+ if (/^\w+}/.test(replacedTemplates)) {
4706
+ throw new PipelineExecutionError('Parameter is not opened');
4707
+ }
4708
+ return replacedTemplates;
4709
+ }
4710
+
4640
4711
  /**
4641
4712
  * Extracts all code blocks from markdown.
4642
4713
  *
@@ -4739,77 +4810,6 @@
4739
4810
  * TODO: [๐Ÿข] Make this logic part of `JsonFormatParser` or `isValidJsonString`
4740
4811
  */
4741
4812
 
4742
- /**
4743
- * Replaces parameters in template with values from parameters object
4744
- *
4745
- * Note: This function is not places strings into string,
4746
- * It's more complex and can handle this operation specifically for LLM models
4747
- *
4748
- * @param template the template with parameters in {curly} braces
4749
- * @param parameters the object with parameters
4750
- * @returns the template with replaced parameters
4751
- * @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
4752
- * @public exported from `@promptbook/utils`
4753
- */
4754
- function templateParameters(template, parameters) {
4755
- for (const [parameterName, parameterValue] of Object.entries(parameters)) {
4756
- if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
4757
- throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
4758
- }
4759
- else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
4760
- // TODO: [๐Ÿต]
4761
- throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
4762
- }
4763
- }
4764
- let replacedTemplates = template;
4765
- let match;
4766
- let loopLimit = LOOP_LIMIT;
4767
- while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
4768
- .exec(replacedTemplates))) {
4769
- if (loopLimit-- < 0) {
4770
- throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
4771
- }
4772
- const precol = match.groups.precol;
4773
- const parameterName = match.groups.parameterName;
4774
- if (parameterName === '') {
4775
- // Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
4776
- continue;
4777
- }
4778
- if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
4779
- throw new PipelineExecutionError('Parameter is already opened or not closed');
4780
- }
4781
- if (parameters[parameterName] === undefined) {
4782
- throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4783
- }
4784
- let parameterValue = parameters[parameterName];
4785
- if (parameterValue === undefined) {
4786
- throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4787
- }
4788
- parameterValue = valueToString(parameterValue);
4789
- // Escape curly braces in parameter values to prevent prompt-injection
4790
- parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4791
- if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
4792
- parameterValue = parameterValue
4793
- .split('\n')
4794
- .map((line, index) => (index === 0 ? line : `${precol}${line}`))
4795
- .join('\n');
4796
- }
4797
- replacedTemplates =
4798
- replacedTemplates.substring(0, match.index + precol.length) +
4799
- parameterValue +
4800
- replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
4801
- }
4802
- // [๐Ÿ’ซ] Check if there are parameters that are not closed properly
4803
- if (/{\w+$/.test(replacedTemplates)) {
4804
- throw new PipelineExecutionError('Parameter is not closed');
4805
- }
4806
- // [๐Ÿ’ซ] Check if there are parameters that are not opened properly
4807
- if (/^\w+}/.test(replacedTemplates)) {
4808
- throw new PipelineExecutionError('Parameter is not opened');
4809
- }
4810
- return replacedTemplates;
4811
- }
4812
-
4813
4813
  /**
4814
4814
  * Counts number of characters in the text
4815
4815
  *
@@ -4970,6 +4970,68 @@
4970
4970
  * Note: [๐Ÿ’] and [๐Ÿค ] are interconnected together
4971
4971
  */
4972
4972
 
4973
+ /**
4974
+ * Validates a prompt result against expectations and format requirements.
4975
+ * This function provides a common abstraction for result validation that can be used
4976
+ * by both execution logic and caching logic to ensure consistency.
4977
+ *
4978
+ * @param options - The validation options including result string, expectations, and format
4979
+ * @returns Validation result with processed string and validity status
4980
+ * @private internal function of `createPipelineExecutor` and `cacheLlmTools`
4981
+ */
4982
+ function validatePromptResult(options) {
4983
+ const { resultString, expectations, format } = options;
4984
+ let processedResultString = resultString;
4985
+ let validationError;
4986
+ try {
4987
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
4988
+ if (format) {
4989
+ if (format === 'JSON') {
4990
+ if (!isValidJsonString(processedResultString)) {
4991
+ // TODO: [๐Ÿข] Do more universally via `FormatParser`
4992
+ try {
4993
+ processedResultString = extractJsonBlock(processedResultString);
4994
+ }
4995
+ catch (error) {
4996
+ keepUnused(error);
4997
+ throw new ExpectError(spaceTrim.spaceTrim((block) => `
4998
+ Expected valid JSON string
4999
+
5000
+ The expected JSON text:
5001
+ ${block(processedResultString)}
5002
+ `));
5003
+ }
5004
+ }
5005
+ }
5006
+ else {
5007
+ throw new UnexpectedError(`Unknown format "${format}"`);
5008
+ }
5009
+ }
5010
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
5011
+ if (expectations) {
5012
+ checkExpectations(expectations, processedResultString);
5013
+ }
5014
+ return {
5015
+ isValid: true,
5016
+ processedResultString,
5017
+ };
5018
+ }
5019
+ catch (error) {
5020
+ if (error instanceof ExpectError) {
5021
+ validationError = error;
5022
+ }
5023
+ else {
5024
+ // Re-throw non-ExpectError errors (like UnexpectedError)
5025
+ throw error;
5026
+ }
5027
+ return {
5028
+ isValid: false,
5029
+ processedResultString,
5030
+ error: validationError,
5031
+ };
5032
+ }
5033
+ }
5034
+
4973
5035
  /**
4974
5036
  * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
4975
5037
  * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
@@ -4992,13 +5054,13 @@
4992
5054
  // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
4993
5055
  const _llms = arrayableToArray(tools.llm);
4994
5056
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
4995
- attempts: for (let attempt = -jokerParameterNames.length; attempt < maxAttempts; attempt++) {
4996
- const isJokerAttempt = attempt < 0;
4997
- const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
5057
+ attempts: for (let attemptIndex = -jokerParameterNames.length; attemptIndex < maxAttempts; attemptIndex++) {
5058
+ const isJokerAttempt = attemptIndex < 0;
5059
+ const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attemptIndex];
4998
5060
  // TODO: [๐Ÿง ][๐Ÿญ] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
4999
5061
  if (isJokerAttempt && !jokerParameterName) {
5000
5062
  throw new UnexpectedError(spaceTrim.spaceTrim((block) => `
5001
- Joker not found in attempt ${attempt}
5063
+ Joker not found in attempt ${attemptIndex}
5002
5064
 
5003
5065
  ${block(pipelineIdentification)}
5004
5066
  `));
@@ -5196,35 +5258,18 @@
5196
5258
  }
5197
5259
  }
5198
5260
  // TODO: [๐Ÿ’] Unite object for expecting amount and format
5199
- if (task.format) {
5200
- if (task.format === 'JSON') {
5201
- if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
5202
- // TODO: [๐Ÿข] Do more universally via `FormatParser`
5203
- try {
5204
- $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
5205
- }
5206
- catch (error) {
5207
- keepUnused(error);
5208
- throw new ExpectError(spaceTrim.spaceTrim((block) => `
5209
- Expected valid JSON string
5210
-
5211
- ${block(
5212
- /*<- Note: No need for `pipelineIdentification`, it will be catched and added later */ '')}
5213
- `));
5214
- }
5215
- }
5216
- }
5217
- else {
5218
- throw new UnexpectedError(spaceTrim.spaceTrim((block) => `
5219
- Unknown format "${task.format}"
5220
-
5221
- ${block(pipelineIdentification)}
5222
- `));
5261
+ // Use the common validation function for both format and expectations
5262
+ if (task.format || task.expectations) {
5263
+ const validationResult = validatePromptResult({
5264
+ resultString: $ongoingTaskResult.$resultString || '',
5265
+ expectations: task.expectations,
5266
+ format: task.format,
5267
+ });
5268
+ if (!validationResult.isValid) {
5269
+ throw validationResult.error;
5223
5270
  }
5224
- }
5225
- // TODO: [๐Ÿ’] Unite object for expecting amount and format
5226
- if (task.expectations) {
5227
- checkExpectations(task.expectations, $ongoingTaskResult.$resultString || '');
5271
+ // Update the result string in case format processing modified it (e.g., JSON extraction)
5272
+ $ongoingTaskResult.$resultString = validationResult.processedResultString;
5228
5273
  }
5229
5274
  break attempts;
5230
5275
  }
@@ -5238,6 +5283,7 @@
5238
5283
  $ongoingTaskResult.$failedResults = [];
5239
5284
  }
5240
5285
  $ongoingTaskResult.$failedResults.push({
5286
+ attemptIndex,
5241
5287
  result: $ongoingTaskResult.$resultString,
5242
5288
  error: error,
5243
5289
  });
@@ -5262,19 +5308,13 @@
5262
5308
  });
5263
5309
  }
5264
5310
  }
5265
- if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
5266
- // Store the current failure before throwing
5267
- $ongoingTaskResult.$failedResults = $ongoingTaskResult.$failedResults || [];
5268
- $ongoingTaskResult.$failedResults.push({
5269
- result: $ongoingTaskResult.$resultString,
5270
- error: $ongoingTaskResult.$expectError,
5271
- });
5272
- // Create a summary of all failures
5311
+ if ($ongoingTaskResult.$expectError !== null && attemptIndex === maxAttempts - 1) {
5312
+ // Note: Create a summary of all failures
5273
5313
  const failuresSummary = $ongoingTaskResult.$failedResults
5274
- .map((failure, index) => spaceTrim.spaceTrim((block) => {
5314
+ .map((failure) => spaceTrim.spaceTrim((block) => {
5275
5315
  var _a, _b;
5276
5316
  return `
5277
- Attempt ${index + 1}:
5317
+ Attempt ${failure.attemptIndex + 1}:
5278
5318
  Error ${((_a = failure.error) === null || _a === void 0 ? void 0 : _a.name) || ''}:
5279
5319
  ${block((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message.split('\n').map((line) => `> ${line}`).join('\n'))}
5280
5320