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