@promptbook/wizard 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
@@ -49,7 +49,7 @@
49
49
  * @generated
50
50
  * @see https://github.com/webgptorg/promptbook
51
51
  */
52
- const PROMPTBOOK_ENGINE_VERSION = '0.98.0-5';
52
+ const PROMPTBOOK_ENGINE_VERSION = '0.98.0-8';
53
53
  /**
54
54
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
55
55
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -243,7 +243,7 @@
243
243
  *
244
244
  * @public exported from `@promptbook/core`
245
245
  */
246
- const DEFAULT_MAX_EXECUTION_ATTEMPTS = 3; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
246
+ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
247
247
  // <- TODO: [๐Ÿ]
248
248
  /**
249
249
  * Where to store your books
@@ -4356,7 +4356,7 @@
4356
4356
  */
4357
4357
 
4358
4358
  /**
4359
- * Execution Tools for calling OpenAI API or other OpeenAI compatible provider
4359
+ * Execution Tools for calling OpenAI API or other OpenAI compatible provider
4360
4360
  *
4361
4361
  * @public exported from `@promptbook/openai`
4362
4362
  */
@@ -4926,6 +4926,7 @@
4926
4926
  baseURL: DEFAULT_OLLAMA_BASE_URL,
4927
4927
  ...ollamaOptions,
4928
4928
  apiKey: 'ollama',
4929
+ isProxied: false, // <- Note: Ollama is always local
4929
4930
  };
4930
4931
  super(openAiCompatibleOptions);
4931
4932
  }
@@ -5112,7 +5113,7 @@
5112
5113
  title: 'Open AI Compatible',
5113
5114
  packageName: '@promptbook/openai',
5114
5115
  className: 'OpenAiCompatibleExecutionTools',
5115
- envVariables: ['OPENAI_API_KEY'],
5116
+ envVariables: ['OPENAI_API_KEY', 'OPENAI_BASE_URL'],
5116
5117
  trustLevel: 'CLOSED',
5117
5118
  order: MODEL_ORDERS.TOP_TIER,
5118
5119
  getBoilerplateConfiguration() {
@@ -5122,11 +5123,35 @@
5122
5123
  className: 'OpenAiCompatibleExecutionTools',
5123
5124
  options: {
5124
5125
  apiKey: 'sk-',
5126
+ baseURL: 'https://api.openai.com/v1',
5127
+ isProxied: false,
5128
+ remoteServerUrl: DEFAULT_REMOTE_SERVER_URL,
5125
5129
  maxRequestsPerMinute: DEFAULT_MAX_REQUESTS_PER_MINUTE,
5126
5130
  },
5127
5131
  };
5128
5132
  },
5129
5133
  createConfigurationFromEnv(env) {
5134
+ // Note: OpenAiCompatibleExecutionTools is an abstract class and cannot be instantiated directly
5135
+ // However, we can provide configuration for users who want to manually instantiate it
5136
+ if (typeof env.OPENAI_API_KEY === 'string') {
5137
+ const options = {
5138
+ apiKey: env.OPENAI_API_KEY,
5139
+ isProxied: false,
5140
+ remoteServerUrl: DEFAULT_REMOTE_SERVER_URL,
5141
+ maxRequestsPerMinute: DEFAULT_MAX_REQUESTS_PER_MINUTE,
5142
+ defaultModelName: 'gpt-4-turbo',
5143
+ };
5144
+ // Add baseURL if provided in environment
5145
+ if (typeof env.OPENAI_BASE_URL === 'string') {
5146
+ options.baseURL = env.OPENAI_BASE_URL;
5147
+ }
5148
+ return {
5149
+ title: 'Open AI Compatible (from env)',
5150
+ packageName: '@promptbook/openai',
5151
+ className: 'OpenAiCompatibleExecutionTools',
5152
+ options,
5153
+ };
5154
+ }
5130
5155
  return null;
5131
5156
  },
5132
5157
  });
@@ -5179,7 +5204,7 @@
5179
5204
  * Default model for chat variant.
5180
5205
  */
5181
5206
  getDefaultChatModel() {
5182
- return this.getDefaultModel('gpt-4o');
5207
+ return this.getDefaultModel('gpt-4-turbo');
5183
5208
  }
5184
5209
  /**
5185
5210
  * Default model for completion variant.
@@ -5209,6 +5234,9 @@
5209
5234
  * @param options which are relevant are directly passed to the OpenAI client
5210
5235
  */
5211
5236
  constructor(options) {
5237
+ if (options.isProxied) {
5238
+ throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI assistants`);
5239
+ }
5212
5240
  super(options);
5213
5241
  this.assistantId = options.assistantId;
5214
5242
  // TODO: [๐Ÿ‘ฑ] Make limiter same as in `OpenAiExecutionTools`
@@ -5390,14 +5418,97 @@
5390
5418
  * @public exported from `@promptbook/openai`
5391
5419
  */
5392
5420
  const createOpenAiCompatibleExecutionTools = Object.assign((options) => {
5421
+ if (options.isProxied) {
5422
+ return new RemoteLlmExecutionTools({
5423
+ ...options,
5424
+ identification: {
5425
+ isAnonymous: true,
5426
+ llmToolsConfiguration: [
5427
+ {
5428
+ title: 'OpenAI Compatible (proxied)',
5429
+ packageName: '@promptbook/openai',
5430
+ className: 'OpenAiCompatibleExecutionTools',
5431
+ options: {
5432
+ ...options,
5433
+ isProxied: false,
5434
+ },
5435
+ },
5436
+ ],
5437
+ },
5438
+ });
5439
+ }
5393
5440
  if (($isRunningInBrowser() || $isRunningInWebWorker()) && !options.dangerouslyAllowBrowser) {
5394
5441
  options = { ...options, dangerouslyAllowBrowser: true };
5395
5442
  }
5396
- return new OpenAiExecutionTools(options);
5443
+ return new HardcodedOpenAiCompatibleExecutionTools(options.defaultModelName, options);
5397
5444
  }, {
5398
5445
  packageName: '@promptbook/openai',
5399
5446
  className: 'OpenAiCompatibleExecutionTools',
5400
5447
  });
5448
+ /**
5449
+ * Execution Tools for calling ONE SPECIFIC PRECONFIGURED OpenAI compatible provider
5450
+ *
5451
+ * @private for `createOpenAiCompatibleExecutionTools`
5452
+ */
5453
+ class HardcodedOpenAiCompatibleExecutionTools extends OpenAiCompatibleExecutionTools {
5454
+ /**
5455
+ * Creates OpenAI compatible Execution Tools.
5456
+ *
5457
+ * @param options which are relevant are directly passed to the OpenAI compatible client
5458
+ */
5459
+ constructor(defaultModelName, options) {
5460
+ super(options);
5461
+ this.defaultModelName = defaultModelName;
5462
+ this.options = options;
5463
+ }
5464
+ get title() {
5465
+ return `${this.defaultModelName} on ${this.options.baseURL}`;
5466
+ }
5467
+ get description() {
5468
+ return `OpenAI compatible connected to "${this.options.baseURL}" model "${this.defaultModelName}"`;
5469
+ }
5470
+ /**
5471
+ * List all available models (non dynamically)
5472
+ *
5473
+ * Note: Purpose of this is to provide more information about models than standard listing from API
5474
+ */
5475
+ get HARDCODED_MODELS() {
5476
+ return [
5477
+ {
5478
+ modelName: this.defaultModelName,
5479
+ modelVariant: 'CHAT',
5480
+ modelDescription: '', // <- TODO: What is the best value here, maybe `this.description`?
5481
+ },
5482
+ ];
5483
+ }
5484
+ /**
5485
+ * Computes the usage
5486
+ */
5487
+ computeUsage(...args) {
5488
+ return {
5489
+ ...computeOpenAiUsage(...args),
5490
+ price: UNCERTAIN_ZERO_VALUE, // <- TODO: Maybe in future pass this counting mechanism, but for now, we dont know
5491
+ };
5492
+ }
5493
+ /**
5494
+ * Default model for chat variant.
5495
+ */
5496
+ getDefaultChatModel() {
5497
+ return this.getDefaultModel(this.defaultModelName);
5498
+ }
5499
+ /**
5500
+ * Default model for completion variant.
5501
+ */
5502
+ getDefaultCompletionModel() {
5503
+ throw new PipelineExecutionError(`${this.title} does not support COMPLETION model variant`);
5504
+ }
5505
+ /**
5506
+ * Default model for completion variant.
5507
+ */
5508
+ getDefaultEmbeddingModel() {
5509
+ throw new PipelineExecutionError(`${this.title} does not support EMBEDDING model variant`);
5510
+ }
5511
+ }
5401
5512
  /**
5402
5513
  * TODO: [๐Ÿฆบ] Is there some way how to put `packageName` and `className` on top and function definition on bottom?
5403
5514
  * TODO: [๐ŸŽถ] Naming "constructor" vs "creator" vs "factory"
@@ -5414,6 +5525,9 @@
5414
5525
  if (($isRunningInBrowser() || $isRunningInWebWorker()) && !options.dangerouslyAllowBrowser) {
5415
5526
  options = { ...options, dangerouslyAllowBrowser: true };
5416
5527
  }
5528
+ if (options.isProxied) {
5529
+ throw new NotYetImplementedError(`Proxy mode is not yet implemented in createOpenAiExecutionTools`);
5530
+ }
5417
5531
  return new OpenAiExecutionTools(options);
5418
5532
  }, {
5419
5533
  packageName: '@promptbook/openai',
@@ -6793,7 +6907,7 @@
6793
6907
  throw new Error(spaceTrim__default["default"]((block) => `
6794
6908
  ${block(error.message)}
6795
6909
 
6796
- The JSON text:
6910
+ The expected JSON text:
6797
6911
  ${block(value)}
6798
6912
  `));
6799
6913
  }
@@ -8702,6 +8816,68 @@
8702
8816
  * Note: [๐Ÿ’] and [๐Ÿค ] are interconnected together
8703
8817
  */
8704
8818
 
8819
+ /**
8820
+ * Validates a prompt result against expectations and format requirements.
8821
+ * This function provides a common abstraction for result validation that can be used
8822
+ * by both execution logic and caching logic to ensure consistency.
8823
+ *
8824
+ * @param options - The validation options including result string, expectations, and format
8825
+ * @returns Validation result with processed string and validity status
8826
+ * @private internal function of `createPipelineExecutor` and `cacheLlmTools`
8827
+ */
8828
+ function validatePromptResult(options) {
8829
+ const { resultString, expectations, format } = options;
8830
+ let processedResultString = resultString;
8831
+ let validationError;
8832
+ try {
8833
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
8834
+ if (format) {
8835
+ if (format === 'JSON') {
8836
+ if (!isValidJsonString(processedResultString)) {
8837
+ // TODO: [๐Ÿข] Do more universally via `FormatParser`
8838
+ try {
8839
+ processedResultString = extractJsonBlock(processedResultString);
8840
+ }
8841
+ catch (error) {
8842
+ keepUnused(error);
8843
+ throw new ExpectError(spaceTrim.spaceTrim((block) => `
8844
+ Expected valid JSON string
8845
+
8846
+ The expected JSON text:
8847
+ ${block(processedResultString)}
8848
+ `));
8849
+ }
8850
+ }
8851
+ }
8852
+ else {
8853
+ throw new UnexpectedError(`Unknown format "${format}"`);
8854
+ }
8855
+ }
8856
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
8857
+ if (expectations) {
8858
+ checkExpectations(expectations, processedResultString);
8859
+ }
8860
+ return {
8861
+ isValid: true,
8862
+ processedResultString,
8863
+ };
8864
+ }
8865
+ catch (error) {
8866
+ if (error instanceof ExpectError) {
8867
+ validationError = error;
8868
+ }
8869
+ else {
8870
+ // Re-throw non-ExpectError errors (like UnexpectedError)
8871
+ throw error;
8872
+ }
8873
+ return {
8874
+ isValid: false,
8875
+ processedResultString,
8876
+ error: validationError,
8877
+ };
8878
+ }
8879
+ }
8880
+
8705
8881
  /**
8706
8882
  * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
8707
8883
  * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
@@ -8724,13 +8900,13 @@
8724
8900
  // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
8725
8901
  const _llms = arrayableToArray(tools.llm);
8726
8902
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
8727
- attempts: for (let attempt = -jokerParameterNames.length; attempt < maxAttempts; attempt++) {
8728
- const isJokerAttempt = attempt < 0;
8729
- const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
8903
+ attempts: for (let attemptIndex = -jokerParameterNames.length; attemptIndex < maxAttempts; attemptIndex++) {
8904
+ const isJokerAttempt = attemptIndex < 0;
8905
+ const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attemptIndex];
8730
8906
  // TODO: [๐Ÿง ][๐Ÿญ] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
8731
8907
  if (isJokerAttempt && !jokerParameterName) {
8732
8908
  throw new UnexpectedError(spaceTrim.spaceTrim((block) => `
8733
- Joker not found in attempt ${attempt}
8909
+ Joker not found in attempt ${attemptIndex}
8734
8910
 
8735
8911
  ${block(pipelineIdentification)}
8736
8912
  `));
@@ -8928,35 +9104,18 @@
8928
9104
  }
8929
9105
  }
8930
9106
  // TODO: [๐Ÿ’] Unite object for expecting amount and format
8931
- if (task.format) {
8932
- if (task.format === 'JSON') {
8933
- if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
8934
- // TODO: [๐Ÿข] Do more universally via `FormatParser`
8935
- try {
8936
- $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
8937
- }
8938
- catch (error) {
8939
- keepUnused(error);
8940
- throw new ExpectError(spaceTrim.spaceTrim((block) => `
8941
- Expected valid JSON string
8942
-
8943
- ${block(
8944
- /*<- Note: No need for `pipelineIdentification`, it will be catched and added later */ '')}
8945
- `));
8946
- }
8947
- }
8948
- }
8949
- else {
8950
- throw new UnexpectedError(spaceTrim.spaceTrim((block) => `
8951
- Unknown format "${task.format}"
8952
-
8953
- ${block(pipelineIdentification)}
8954
- `));
9107
+ // Use the common validation function for both format and expectations
9108
+ if (task.format || task.expectations) {
9109
+ const validationResult = validatePromptResult({
9110
+ resultString: $ongoingTaskResult.$resultString || '',
9111
+ expectations: task.expectations,
9112
+ format: task.format,
9113
+ });
9114
+ if (!validationResult.isValid) {
9115
+ throw validationResult.error;
8955
9116
  }
8956
- }
8957
- // TODO: [๐Ÿ’] Unite object for expecting amount and format
8958
- if (task.expectations) {
8959
- checkExpectations(task.expectations, $ongoingTaskResult.$resultString || '');
9117
+ // Update the result string in case format processing modified it (e.g., JSON extraction)
9118
+ $ongoingTaskResult.$resultString = validationResult.processedResultString;
8960
9119
  }
8961
9120
  break attempts;
8962
9121
  }
@@ -8970,6 +9129,7 @@
8970
9129
  $ongoingTaskResult.$failedResults = [];
8971
9130
  }
8972
9131
  $ongoingTaskResult.$failedResults.push({
9132
+ attemptIndex,
8973
9133
  result: $ongoingTaskResult.$resultString,
8974
9134
  error: error,
8975
9135
  });
@@ -8994,19 +9154,13 @@
8994
9154
  });
8995
9155
  }
8996
9156
  }
8997
- if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
8998
- // Store the current failure before throwing
8999
- $ongoingTaskResult.$failedResults = $ongoingTaskResult.$failedResults || [];
9000
- $ongoingTaskResult.$failedResults.push({
9001
- result: $ongoingTaskResult.$resultString,
9002
- error: $ongoingTaskResult.$expectError,
9003
- });
9004
- // Create a summary of all failures
9157
+ if ($ongoingTaskResult.$expectError !== null && attemptIndex === maxAttempts - 1) {
9158
+ // Note: Create a summary of all failures
9005
9159
  const failuresSummary = $ongoingTaskResult.$failedResults
9006
- .map((failure, index) => spaceTrim.spaceTrim((block) => {
9160
+ .map((failure) => spaceTrim.spaceTrim((block) => {
9007
9161
  var _a, _b;
9008
9162
  return `
9009
- Attempt ${index + 1}:
9163
+ Attempt ${failure.attemptIndex + 1}:
9010
9164
  Error ${((_a = failure.error) === null || _a === void 0 ? void 0 : _a.name) || ''}:
9011
9165
  ${block((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message.split('\n').map((line) => `> ${line}`).join('\n'))}
9012
9166
 
@@ -11813,6 +11967,7 @@
11813
11967
  },
11814
11968
  };
11815
11969
  const callCommonModel = async (prompt) => {
11970
+ var _a;
11816
11971
  const { parameters, content, modelRequirements } = prompt;
11817
11972
  // <- Note: These are relevant things from the prompt that the cache key should depend on.
11818
11973
  // TODO: Maybe some standalone function for normalization of content for cache
@@ -11868,11 +12023,42 @@
11868
12023
  // 1. It has a content property that is null or undefined
11869
12024
  // 2. It has an error property that is truthy
11870
12025
  // 3. It has a success property that is explicitly false
11871
- const isFailedResult = promptResult.content === null ||
12026
+ // 4. It doesn't meet the prompt's expectations or format requirements
12027
+ const isBasicFailedResult = promptResult.content === null ||
11872
12028
  promptResult.content === undefined ||
11873
12029
  promptResult.error ||
11874
12030
  promptResult.success === false;
11875
- if (!isFailedResult) {
12031
+ let shouldCache = !isBasicFailedResult;
12032
+ // If the basic result is valid, check against expectations and format
12033
+ if (shouldCache && promptResult.content) {
12034
+ try {
12035
+ const validationResult = validatePromptResult({
12036
+ resultString: promptResult.content,
12037
+ expectations: prompt.expectations,
12038
+ format: prompt.format,
12039
+ });
12040
+ shouldCache = validationResult.isValid;
12041
+ if (!shouldCache && isVerbose) {
12042
+ console.info('Not caching result that fails expectations/format validation for key:', key, {
12043
+ content: promptResult.content,
12044
+ expectations: prompt.expectations,
12045
+ format: prompt.format,
12046
+ validationError: (_a = validationResult.error) === null || _a === void 0 ? void 0 : _a.message,
12047
+ });
12048
+ }
12049
+ }
12050
+ catch (error) {
12051
+ // If validation throws an unexpected error, don't cache
12052
+ shouldCache = false;
12053
+ if (isVerbose) {
12054
+ console.info('Not caching result due to validation error for key:', key, {
12055
+ content: promptResult.content,
12056
+ validationError: error instanceof Error ? error.message : String(error),
12057
+ });
12058
+ }
12059
+ }
12060
+ }
12061
+ if (shouldCache) {
11876
12062
  await storage.setItem(key, {
11877
12063
  date: $getCurrentDate(),
11878
12064
  promptbookVersion: PROMPTBOOK_ENGINE_VERSION,
@@ -11889,7 +12075,7 @@
11889
12075
  promptResult,
11890
12076
  });
11891
12077
  }
11892
- else if (isVerbose) {
12078
+ else if (isVerbose && isBasicFailedResult) {
11893
12079
  console.info('Not caching failed result for key:', key, {
11894
12080
  content: promptResult.content,
11895
12081
  error: promptResult.error,