@promptbook/website-crawler 0.98.0-5 โ†’ 0.98.0-9

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/esm/index.es.js CHANGED
@@ -29,7 +29,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
29
29
  * @generated
30
30
  * @see https://github.com/webgptorg/promptbook
31
31
  */
32
- const PROMPTBOOK_ENGINE_VERSION = '0.98.0-5';
32
+ const PROMPTBOOK_ENGINE_VERSION = '0.98.0-9';
33
33
  /**
34
34
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
35
35
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -205,7 +205,7 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
205
205
  *
206
206
  * @public exported from `@promptbook/core`
207
207
  */
208
- const DEFAULT_MAX_EXECUTION_ATTEMPTS = 3; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
208
+ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
209
209
  // <- TODO: [๐Ÿ•] Make also `BOOKS_DIRNAME_ALTERNATIVES`
210
210
  // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
211
211
  /**
@@ -2517,7 +2517,7 @@ function jsonParse(value) {
2517
2517
  throw new Error(spaceTrim$1((block) => `
2518
2518
  ${block(error.message)}
2519
2519
 
2520
- The JSON text:
2520
+ The expected JSON text:
2521
2521
  ${block(value)}
2522
2522
  `));
2523
2523
  }
@@ -4489,6 +4489,77 @@ function mapAvailableToExpectedParameters(options) {
4489
4489
  return mappedParameters;
4490
4490
  }
4491
4491
 
4492
+ /**
4493
+ * Replaces parameters in template with values from parameters object
4494
+ *
4495
+ * Note: This function is not places strings into string,
4496
+ * It's more complex and can handle this operation specifically for LLM models
4497
+ *
4498
+ * @param template the template with parameters in {curly} braces
4499
+ * @param parameters the object with parameters
4500
+ * @returns the template with replaced parameters
4501
+ * @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
4502
+ * @public exported from `@promptbook/utils`
4503
+ */
4504
+ function templateParameters(template, parameters) {
4505
+ for (const [parameterName, parameterValue] of Object.entries(parameters)) {
4506
+ if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
4507
+ throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
4508
+ }
4509
+ else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
4510
+ // TODO: [๐Ÿต]
4511
+ throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
4512
+ }
4513
+ }
4514
+ let replacedTemplates = template;
4515
+ let match;
4516
+ let loopLimit = LOOP_LIMIT;
4517
+ while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
4518
+ .exec(replacedTemplates))) {
4519
+ if (loopLimit-- < 0) {
4520
+ throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
4521
+ }
4522
+ const precol = match.groups.precol;
4523
+ const parameterName = match.groups.parameterName;
4524
+ if (parameterName === '') {
4525
+ // Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
4526
+ continue;
4527
+ }
4528
+ if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
4529
+ throw new PipelineExecutionError('Parameter is already opened or not closed');
4530
+ }
4531
+ if (parameters[parameterName] === undefined) {
4532
+ throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4533
+ }
4534
+ let parameterValue = parameters[parameterName];
4535
+ if (parameterValue === undefined) {
4536
+ throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4537
+ }
4538
+ parameterValue = valueToString(parameterValue);
4539
+ // Escape curly braces in parameter values to prevent prompt-injection
4540
+ parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4541
+ if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
4542
+ parameterValue = parameterValue
4543
+ .split('\n')
4544
+ .map((line, index) => (index === 0 ? line : `${precol}${line}`))
4545
+ .join('\n');
4546
+ }
4547
+ replacedTemplates =
4548
+ replacedTemplates.substring(0, match.index + precol.length) +
4549
+ parameterValue +
4550
+ replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
4551
+ }
4552
+ // [๐Ÿ’ซ] Check if there are parameters that are not closed properly
4553
+ if (/{\w+$/.test(replacedTemplates)) {
4554
+ throw new PipelineExecutionError('Parameter is not closed');
4555
+ }
4556
+ // [๐Ÿ’ซ] Check if there are parameters that are not opened properly
4557
+ if (/^\w+}/.test(replacedTemplates)) {
4558
+ throw new PipelineExecutionError('Parameter is not opened');
4559
+ }
4560
+ return replacedTemplates;
4561
+ }
4562
+
4492
4563
  /**
4493
4564
  * Extracts all code blocks from markdown.
4494
4565
  *
@@ -4591,77 +4662,6 @@ function extractJsonBlock(markdown) {
4591
4662
  * TODO: [๐Ÿข] Make this logic part of `JsonFormatParser` or `isValidJsonString`
4592
4663
  */
4593
4664
 
4594
- /**
4595
- * Replaces parameters in template with values from parameters object
4596
- *
4597
- * Note: This function is not places strings into string,
4598
- * It's more complex and can handle this operation specifically for LLM models
4599
- *
4600
- * @param template the template with parameters in {curly} braces
4601
- * @param parameters the object with parameters
4602
- * @returns the template with replaced parameters
4603
- * @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
4604
- * @public exported from `@promptbook/utils`
4605
- */
4606
- function templateParameters(template, parameters) {
4607
- for (const [parameterName, parameterValue] of Object.entries(parameters)) {
4608
- if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
4609
- throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
4610
- }
4611
- else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
4612
- // TODO: [๐Ÿต]
4613
- throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
4614
- }
4615
- }
4616
- let replacedTemplates = template;
4617
- let match;
4618
- let loopLimit = LOOP_LIMIT;
4619
- while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
4620
- .exec(replacedTemplates))) {
4621
- if (loopLimit-- < 0) {
4622
- throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
4623
- }
4624
- const precol = match.groups.precol;
4625
- const parameterName = match.groups.parameterName;
4626
- if (parameterName === '') {
4627
- // Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
4628
- continue;
4629
- }
4630
- if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
4631
- throw new PipelineExecutionError('Parameter is already opened or not closed');
4632
- }
4633
- if (parameters[parameterName] === undefined) {
4634
- throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4635
- }
4636
- let parameterValue = parameters[parameterName];
4637
- if (parameterValue === undefined) {
4638
- throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4639
- }
4640
- parameterValue = valueToString(parameterValue);
4641
- // Escape curly braces in parameter values to prevent prompt-injection
4642
- parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4643
- if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
4644
- parameterValue = parameterValue
4645
- .split('\n')
4646
- .map((line, index) => (index === 0 ? line : `${precol}${line}`))
4647
- .join('\n');
4648
- }
4649
- replacedTemplates =
4650
- replacedTemplates.substring(0, match.index + precol.length) +
4651
- parameterValue +
4652
- replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
4653
- }
4654
- // [๐Ÿ’ซ] Check if there are parameters that are not closed properly
4655
- if (/{\w+$/.test(replacedTemplates)) {
4656
- throw new PipelineExecutionError('Parameter is not closed');
4657
- }
4658
- // [๐Ÿ’ซ] Check if there are parameters that are not opened properly
4659
- if (/^\w+}/.test(replacedTemplates)) {
4660
- throw new PipelineExecutionError('Parameter is not opened');
4661
- }
4662
- return replacedTemplates;
4663
- }
4664
-
4665
4665
  /**
4666
4666
  * Counts number of characters in the text
4667
4667
  *
@@ -4822,6 +4822,68 @@ function checkExpectations(expectations, value) {
4822
4822
  * Note: [๐Ÿ’] and [๐Ÿค ] are interconnected together
4823
4823
  */
4824
4824
 
4825
+ /**
4826
+ * Validates a prompt result against expectations and format requirements.
4827
+ * This function provides a common abstraction for result validation that can be used
4828
+ * by both execution logic and caching logic to ensure consistency.
4829
+ *
4830
+ * @param options - The validation options including result string, expectations, and format
4831
+ * @returns Validation result with processed string and validity status
4832
+ * @private internal function of `createPipelineExecutor` and `cacheLlmTools`
4833
+ */
4834
+ function validatePromptResult(options) {
4835
+ const { resultString, expectations, format } = options;
4836
+ let processedResultString = resultString;
4837
+ let validationError;
4838
+ try {
4839
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
4840
+ if (format) {
4841
+ if (format === 'JSON') {
4842
+ if (!isValidJsonString(processedResultString)) {
4843
+ // TODO: [๐Ÿข] Do more universally via `FormatParser`
4844
+ try {
4845
+ processedResultString = extractJsonBlock(processedResultString);
4846
+ }
4847
+ catch (error) {
4848
+ keepUnused(error);
4849
+ throw new ExpectError(spaceTrim((block) => `
4850
+ Expected valid JSON string
4851
+
4852
+ The expected JSON text:
4853
+ ${block(processedResultString)}
4854
+ `));
4855
+ }
4856
+ }
4857
+ }
4858
+ else {
4859
+ throw new UnexpectedError(`Unknown format "${format}"`);
4860
+ }
4861
+ }
4862
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
4863
+ if (expectations) {
4864
+ checkExpectations(expectations, processedResultString);
4865
+ }
4866
+ return {
4867
+ isValid: true,
4868
+ processedResultString,
4869
+ };
4870
+ }
4871
+ catch (error) {
4872
+ if (error instanceof ExpectError) {
4873
+ validationError = error;
4874
+ }
4875
+ else {
4876
+ // Re-throw non-ExpectError errors (like UnexpectedError)
4877
+ throw error;
4878
+ }
4879
+ return {
4880
+ isValid: false,
4881
+ processedResultString,
4882
+ error: validationError,
4883
+ };
4884
+ }
4885
+ }
4886
+
4825
4887
  /**
4826
4888
  * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
4827
4889
  * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
@@ -4844,13 +4906,13 @@ async function executeAttempts(options) {
4844
4906
  // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
4845
4907
  const _llms = arrayableToArray(tools.llm);
4846
4908
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
4847
- attempts: for (let attempt = -jokerParameterNames.length; attempt < maxAttempts; attempt++) {
4848
- const isJokerAttempt = attempt < 0;
4849
- const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
4909
+ attempts: for (let attemptIndex = -jokerParameterNames.length; attemptIndex < maxAttempts; attemptIndex++) {
4910
+ const isJokerAttempt = attemptIndex < 0;
4911
+ const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attemptIndex];
4850
4912
  // TODO: [๐Ÿง ][๐Ÿญ] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
4851
4913
  if (isJokerAttempt && !jokerParameterName) {
4852
4914
  throw new UnexpectedError(spaceTrim((block) => `
4853
- Joker not found in attempt ${attempt}
4915
+ Joker not found in attempt ${attemptIndex}
4854
4916
 
4855
4917
  ${block(pipelineIdentification)}
4856
4918
  `));
@@ -5048,35 +5110,18 @@ async function executeAttempts(options) {
5048
5110
  }
5049
5111
  }
5050
5112
  // TODO: [๐Ÿ’] Unite object for expecting amount and format
5051
- if (task.format) {
5052
- if (task.format === 'JSON') {
5053
- if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
5054
- // TODO: [๐Ÿข] Do more universally via `FormatParser`
5055
- try {
5056
- $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
5057
- }
5058
- catch (error) {
5059
- keepUnused(error);
5060
- throw new ExpectError(spaceTrim((block) => `
5061
- Expected valid JSON string
5062
-
5063
- ${block(
5064
- /*<- Note: No need for `pipelineIdentification`, it will be catched and added later */ '')}
5065
- `));
5066
- }
5067
- }
5068
- }
5069
- else {
5070
- throw new UnexpectedError(spaceTrim((block) => `
5071
- Unknown format "${task.format}"
5072
-
5073
- ${block(pipelineIdentification)}
5074
- `));
5113
+ // Use the common validation function for both format and expectations
5114
+ if (task.format || task.expectations) {
5115
+ const validationResult = validatePromptResult({
5116
+ resultString: $ongoingTaskResult.$resultString || '',
5117
+ expectations: task.expectations,
5118
+ format: task.format,
5119
+ });
5120
+ if (!validationResult.isValid) {
5121
+ throw validationResult.error;
5075
5122
  }
5076
- }
5077
- // TODO: [๐Ÿ’] Unite object for expecting amount and format
5078
- if (task.expectations) {
5079
- checkExpectations(task.expectations, $ongoingTaskResult.$resultString || '');
5123
+ // Update the result string in case format processing modified it (e.g., JSON extraction)
5124
+ $ongoingTaskResult.$resultString = validationResult.processedResultString;
5080
5125
  }
5081
5126
  break attempts;
5082
5127
  }
@@ -5090,6 +5135,7 @@ async function executeAttempts(options) {
5090
5135
  $ongoingTaskResult.$failedResults = [];
5091
5136
  }
5092
5137
  $ongoingTaskResult.$failedResults.push({
5138
+ attemptIndex,
5093
5139
  result: $ongoingTaskResult.$resultString,
5094
5140
  error: error,
5095
5141
  });
@@ -5114,19 +5160,13 @@ async function executeAttempts(options) {
5114
5160
  });
5115
5161
  }
5116
5162
  }
5117
- if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
5118
- // Store the current failure before throwing
5119
- $ongoingTaskResult.$failedResults = $ongoingTaskResult.$failedResults || [];
5120
- $ongoingTaskResult.$failedResults.push({
5121
- result: $ongoingTaskResult.$resultString,
5122
- error: $ongoingTaskResult.$expectError,
5123
- });
5124
- // Create a summary of all failures
5163
+ if ($ongoingTaskResult.$expectError !== null && attemptIndex === maxAttempts - 1) {
5164
+ // Note: Create a summary of all failures
5125
5165
  const failuresSummary = $ongoingTaskResult.$failedResults
5126
- .map((failure, index) => spaceTrim((block) => {
5166
+ .map((failure) => spaceTrim((block) => {
5127
5167
  var _a, _b;
5128
5168
  return `
5129
- Attempt ${index + 1}:
5169
+ Attempt ${failure.attemptIndex + 1}:
5130
5170
  Error ${((_a = failure.error) === null || _a === void 0 ? void 0 : _a.name) || ''}:
5131
5171
  ${block((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message.split('\n').map((line) => `> ${line}`).join('\n'))}
5132
5172