@promptbook/remote-server 0.95.0 โ†’ 0.98.0-10

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.
Files changed (25) hide show
  1. package/README.md +12 -0
  2. package/esm/index.es.js +303 -181
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/src/_packages/anthropic-claude.index.d.ts +2 -2
  5. package/esm/typings/src/_packages/cli.index.d.ts +4 -0
  6. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  7. package/esm/typings/src/_packages/openai.index.d.ts +10 -0
  8. package/esm/typings/src/_packages/types.index.d.ts +12 -2
  9. package/esm/typings/src/_packages/wizard.index.d.ts +4 -0
  10. package/esm/typings/src/config.d.ts +1 -1
  11. package/esm/typings/src/execution/createPipelineExecutor/$OngoingTaskResult.d.ts +8 -0
  12. package/esm/typings/src/execution/utils/validatePromptResult.d.ts +53 -0
  13. package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +3 -3
  14. package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionToolsOptions.d.ts +2 -2
  15. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionToolsOptions.d.ts +2 -2
  16. package/esm/typings/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +4 -4
  17. package/esm/typings/src/llm-providers/openai/OpenAiCompatibleExecutionToolsOptions.d.ts +52 -0
  18. package/esm/typings/src/llm-providers/openai/OpenAiExecutionToolsOptions.d.ts +3 -5
  19. package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +74 -0
  20. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +11 -0
  21. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +14 -0
  22. package/esm/typings/src/version.d.ts +1 -1
  23. package/package.json +2 -2
  24. package/umd/index.umd.js +303 -181
  25. package/umd/index.umd.js.map +1 -1
package/umd/index.umd.js CHANGED
@@ -48,7 +48,7 @@
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.95.0';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.98.0-10';
52
52
  /**
53
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
54
54
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -188,7 +188,7 @@
188
188
  *
189
189
  * @public exported from `@promptbook/core`
190
190
  */
191
- const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
191
+ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
192
192
  // <- TODO: [๐Ÿ•] Make also `BOOKS_DIRNAME_ALTERNATIVES`
193
193
  // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
194
194
  /**
@@ -1906,7 +1906,7 @@
1906
1906
  throw new Error(spaceTrim__default["default"]((block) => `
1907
1907
  ${block(error.message)}
1908
1908
 
1909
- The JSON text:
1909
+ The expected JSON text:
1910
1910
  ${block(value)}
1911
1911
  `));
1912
1912
  }
@@ -4828,6 +4828,94 @@
4828
4828
  return mappedParameters;
4829
4829
  }
4830
4830
 
4831
+ /**
4832
+ * Just says that the variable is not used but should be kept
4833
+ * No side effects.
4834
+ *
4835
+ * Note: It can be useful for:
4836
+ *
4837
+ * 1) Suppressing eager optimization of unused imports
4838
+ * 2) Suppressing eslint errors of unused variables in the tests
4839
+ * 3) Keeping the type of the variable for type testing
4840
+ *
4841
+ * @param value any values
4842
+ * @returns void
4843
+ * @private within the repository
4844
+ */
4845
+ function keepUnused(...valuesToKeep) {
4846
+ }
4847
+
4848
+ /**
4849
+ * Replaces parameters in template with values from parameters object
4850
+ *
4851
+ * Note: This function is not places strings into string,
4852
+ * It's more complex and can handle this operation specifically for LLM models
4853
+ *
4854
+ * @param template the template with parameters in {curly} braces
4855
+ * @param parameters the object with parameters
4856
+ * @returns the template with replaced parameters
4857
+ * @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
4858
+ * @public exported from `@promptbook/utils`
4859
+ */
4860
+ function templateParameters(template, parameters) {
4861
+ for (const [parameterName, parameterValue] of Object.entries(parameters)) {
4862
+ if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
4863
+ throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
4864
+ }
4865
+ else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
4866
+ // TODO: [๐Ÿต]
4867
+ throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
4868
+ }
4869
+ }
4870
+ let replacedTemplates = template;
4871
+ let match;
4872
+ let loopLimit = LOOP_LIMIT;
4873
+ while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
4874
+ .exec(replacedTemplates))) {
4875
+ if (loopLimit-- < 0) {
4876
+ throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
4877
+ }
4878
+ const precol = match.groups.precol;
4879
+ const parameterName = match.groups.parameterName;
4880
+ if (parameterName === '') {
4881
+ // Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
4882
+ continue;
4883
+ }
4884
+ if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
4885
+ throw new PipelineExecutionError('Parameter is already opened or not closed');
4886
+ }
4887
+ if (parameters[parameterName] === undefined) {
4888
+ throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4889
+ }
4890
+ let parameterValue = parameters[parameterName];
4891
+ if (parameterValue === undefined) {
4892
+ throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4893
+ }
4894
+ parameterValue = valueToString(parameterValue);
4895
+ // Escape curly braces in parameter values to prevent prompt-injection
4896
+ parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4897
+ if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
4898
+ parameterValue = parameterValue
4899
+ .split('\n')
4900
+ .map((line, index) => (index === 0 ? line : `${precol}${line}`))
4901
+ .join('\n');
4902
+ }
4903
+ replacedTemplates =
4904
+ replacedTemplates.substring(0, match.index + precol.length) +
4905
+ parameterValue +
4906
+ replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
4907
+ }
4908
+ // [๐Ÿ’ซ] Check if there are parameters that are not closed properly
4909
+ if (/{\w+$/.test(replacedTemplates)) {
4910
+ throw new PipelineExecutionError('Parameter is not closed');
4911
+ }
4912
+ // [๐Ÿ’ซ] Check if there are parameters that are not opened properly
4913
+ if (/^\w+}/.test(replacedTemplates)) {
4914
+ throw new PipelineExecutionError('Parameter is not opened');
4915
+ }
4916
+ return replacedTemplates;
4917
+ }
4918
+
4831
4919
  /**
4832
4920
  * Extracts all code blocks from markdown.
4833
4921
  *
@@ -4930,94 +5018,6 @@
4930
5018
  * TODO: [๐Ÿข] Make this logic part of `JsonFormatParser` or `isValidJsonString`
4931
5019
  */
4932
5020
 
4933
- /**
4934
- * Just says that the variable is not used but should be kept
4935
- * No side effects.
4936
- *
4937
- * Note: It can be useful for:
4938
- *
4939
- * 1) Suppressing eager optimization of unused imports
4940
- * 2) Suppressing eslint errors of unused variables in the tests
4941
- * 3) Keeping the type of the variable for type testing
4942
- *
4943
- * @param value any values
4944
- * @returns void
4945
- * @private within the repository
4946
- */
4947
- function keepUnused(...valuesToKeep) {
4948
- }
4949
-
4950
- /**
4951
- * Replaces parameters in template with values from parameters object
4952
- *
4953
- * Note: This function is not places strings into string,
4954
- * It's more complex and can handle this operation specifically for LLM models
4955
- *
4956
- * @param template the template with parameters in {curly} braces
4957
- * @param parameters the object with parameters
4958
- * @returns the template with replaced parameters
4959
- * @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
4960
- * @public exported from `@promptbook/utils`
4961
- */
4962
- function templateParameters(template, parameters) {
4963
- for (const [parameterName, parameterValue] of Object.entries(parameters)) {
4964
- if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
4965
- throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
4966
- }
4967
- else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
4968
- // TODO: [๐Ÿต]
4969
- throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
4970
- }
4971
- }
4972
- let replacedTemplates = template;
4973
- let match;
4974
- let loopLimit = LOOP_LIMIT;
4975
- while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
4976
- .exec(replacedTemplates))) {
4977
- if (loopLimit-- < 0) {
4978
- throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
4979
- }
4980
- const precol = match.groups.precol;
4981
- const parameterName = match.groups.parameterName;
4982
- if (parameterName === '') {
4983
- // Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
4984
- continue;
4985
- }
4986
- if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
4987
- throw new PipelineExecutionError('Parameter is already opened or not closed');
4988
- }
4989
- if (parameters[parameterName] === undefined) {
4990
- throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4991
- }
4992
- let parameterValue = parameters[parameterName];
4993
- if (parameterValue === undefined) {
4994
- throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4995
- }
4996
- parameterValue = valueToString(parameterValue);
4997
- // Escape curly braces in parameter values to prevent prompt-injection
4998
- parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4999
- if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
5000
- parameterValue = parameterValue
5001
- .split('\n')
5002
- .map((line, index) => (index === 0 ? line : `${precol}${line}`))
5003
- .join('\n');
5004
- }
5005
- replacedTemplates =
5006
- replacedTemplates.substring(0, match.index + precol.length) +
5007
- parameterValue +
5008
- replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
5009
- }
5010
- // [๐Ÿ’ซ] Check if there are parameters that are not closed properly
5011
- if (/{\w+$/.test(replacedTemplates)) {
5012
- throw new PipelineExecutionError('Parameter is not closed');
5013
- }
5014
- // [๐Ÿ’ซ] Check if there are parameters that are not opened properly
5015
- if (/^\w+}/.test(replacedTemplates)) {
5016
- throw new PipelineExecutionError('Parameter is not opened');
5017
- }
5018
- return replacedTemplates;
5019
- }
5020
-
5021
5021
  /**
5022
5022
  * Counts number of characters in the text
5023
5023
  *
@@ -5178,6 +5178,68 @@
5178
5178
  * Note: [๐Ÿ’] and [๐Ÿค ] are interconnected together
5179
5179
  */
5180
5180
 
5181
+ /**
5182
+ * Validates a prompt result against expectations and format requirements.
5183
+ * This function provides a common abstraction for result validation that can be used
5184
+ * by both execution logic and caching logic to ensure consistency.
5185
+ *
5186
+ * @param options - The validation options including result string, expectations, and format
5187
+ * @returns Validation result with processed string and validity status
5188
+ * @private internal function of `createPipelineExecutor` and `cacheLlmTools`
5189
+ */
5190
+ function validatePromptResult(options) {
5191
+ const { resultString, expectations, format } = options;
5192
+ let processedResultString = resultString;
5193
+ let validationError;
5194
+ try {
5195
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
5196
+ if (format) {
5197
+ if (format === 'JSON') {
5198
+ if (!isValidJsonString(processedResultString)) {
5199
+ // TODO: [๐Ÿข] Do more universally via `FormatParser`
5200
+ try {
5201
+ processedResultString = extractJsonBlock(processedResultString);
5202
+ }
5203
+ catch (error) {
5204
+ keepUnused(error);
5205
+ throw new ExpectError(spaceTrim.spaceTrim((block) => `
5206
+ Expected valid JSON string
5207
+
5208
+ The expected JSON text:
5209
+ ${block(processedResultString)}
5210
+ `));
5211
+ }
5212
+ }
5213
+ }
5214
+ else {
5215
+ throw new UnexpectedError(`Unknown format "${format}"`);
5216
+ }
5217
+ }
5218
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
5219
+ if (expectations) {
5220
+ checkExpectations(expectations, processedResultString);
5221
+ }
5222
+ return {
5223
+ isValid: true,
5224
+ processedResultString,
5225
+ };
5226
+ }
5227
+ catch (error) {
5228
+ if (error instanceof ExpectError) {
5229
+ validationError = error;
5230
+ }
5231
+ else {
5232
+ // Re-throw non-ExpectError errors (like UnexpectedError)
5233
+ throw error;
5234
+ }
5235
+ return {
5236
+ isValid: false,
5237
+ processedResultString,
5238
+ error: validationError,
5239
+ };
5240
+ }
5241
+ }
5242
+
5181
5243
  /**
5182
5244
  * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
5183
5245
  * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
@@ -5195,17 +5257,18 @@
5195
5257
  $resultString: null,
5196
5258
  $expectError: null,
5197
5259
  $scriptPipelineExecutionErrors: [],
5260
+ $failedResults: [], // Track all failed attempts
5198
5261
  };
5199
5262
  // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
5200
5263
  const _llms = arrayableToArray(tools.llm);
5201
5264
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
5202
- attempts: for (let attempt = -jokerParameterNames.length; attempt < maxAttempts; attempt++) {
5203
- const isJokerAttempt = attempt < 0;
5204
- const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
5265
+ attempts: for (let attemptIndex = -jokerParameterNames.length; attemptIndex < maxAttempts; attemptIndex++) {
5266
+ const isJokerAttempt = attemptIndex < 0;
5267
+ const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attemptIndex];
5205
5268
  // TODO: [๐Ÿง ][๐Ÿญ] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
5206
5269
  if (isJokerAttempt && !jokerParameterName) {
5207
5270
  throw new UnexpectedError(spaceTrim.spaceTrim((block) => `
5208
- Joker not found in attempt ${attempt}
5271
+ Joker not found in attempt ${attemptIndex}
5209
5272
 
5210
5273
  ${block(pipelineIdentification)}
5211
5274
  `));
@@ -5403,35 +5466,18 @@
5403
5466
  }
5404
5467
  }
5405
5468
  // TODO: [๐Ÿ’] Unite object for expecting amount and format
5406
- if (task.format) {
5407
- if (task.format === 'JSON') {
5408
- if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
5409
- // TODO: [๐Ÿข] Do more universally via `FormatParser`
5410
- try {
5411
- $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
5412
- }
5413
- catch (error) {
5414
- keepUnused(error);
5415
- throw new ExpectError(spaceTrim.spaceTrim((block) => `
5416
- Expected valid JSON string
5417
-
5418
- ${block(
5419
- /*<- Note: No need for `pipelineIdentification`, it will be catched and added later */ '')}
5420
- `));
5421
- }
5422
- }
5423
- }
5424
- else {
5425
- throw new UnexpectedError(spaceTrim.spaceTrim((block) => `
5426
- Unknown format "${task.format}"
5427
-
5428
- ${block(pipelineIdentification)}
5429
- `));
5469
+ // Use the common validation function for both format and expectations
5470
+ if (task.format || task.expectations) {
5471
+ const validationResult = validatePromptResult({
5472
+ resultString: $ongoingTaskResult.$resultString || '',
5473
+ expectations: task.expectations,
5474
+ format: task.format,
5475
+ });
5476
+ if (!validationResult.isValid) {
5477
+ throw validationResult.error;
5430
5478
  }
5431
- }
5432
- // TODO: [๐Ÿ’] Unite object for expecting amount and format
5433
- if (task.expectations) {
5434
- checkExpectations(task.expectations, $ongoingTaskResult.$resultString || '');
5479
+ // Update the result string in case format processing modified it (e.g., JSON extraction)
5480
+ $ongoingTaskResult.$resultString = validationResult.processedResultString;
5435
5481
  }
5436
5482
  break attempts;
5437
5483
  }
@@ -5440,6 +5486,15 @@
5440
5486
  throw error;
5441
5487
  }
5442
5488
  $ongoingTaskResult.$expectError = error;
5489
+ // Store each failed attempt
5490
+ if (!Array.isArray($ongoingTaskResult.$failedResults)) {
5491
+ $ongoingTaskResult.$failedResults = [];
5492
+ }
5493
+ $ongoingTaskResult.$failedResults.push({
5494
+ attemptIndex,
5495
+ result: $ongoingTaskResult.$resultString,
5496
+ error: error,
5497
+ });
5443
5498
  }
5444
5499
  finally {
5445
5500
  if (!isJokerAttempt &&
@@ -5461,35 +5516,41 @@
5461
5516
  });
5462
5517
  }
5463
5518
  }
5464
- if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
5519
+ if ($ongoingTaskResult.$expectError !== null && attemptIndex === maxAttempts - 1) {
5520
+ // Note: Create a summary of all failures
5521
+ const failuresSummary = $ongoingTaskResult.$failedResults
5522
+ .map((failure) => spaceTrim.spaceTrim((block) => {
5523
+ var _a, _b;
5524
+ return `
5525
+ Attempt ${failure.attemptIndex + 1}:
5526
+ Error ${((_a = failure.error) === null || _a === void 0 ? void 0 : _a.name) || ''}:
5527
+ ${block((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message.split('\n').map((line) => `> ${line}`).join('\n'))}
5528
+
5529
+ Result:
5530
+ ${block(failure.result === null
5531
+ ? 'null'
5532
+ : spaceTrim.spaceTrim(failure.result)
5533
+ .split('\n')
5534
+ .map((line) => `> ${line}`)
5535
+ .join('\n'))}
5536
+ `;
5537
+ }))
5538
+ .join('\n\n---\n\n');
5465
5539
  throw new PipelineExecutionError(spaceTrim.spaceTrim((block) => {
5466
- var _a, _b, _c;
5540
+ var _a;
5467
5541
  return `
5468
5542
  LLM execution failed ${maxExecutionAttempts}x
5469
5543
 
5470
5544
  ${block(pipelineIdentification)}
5471
5545
 
5472
- ---
5473
5546
  The Prompt:
5474
5547
  ${block((((_a = $ongoingTaskResult.$prompt) === null || _a === void 0 ? void 0 : _a.content) || '')
5475
5548
  .split('\n')
5476
5549
  .map((line) => `> ${line}`)
5477
5550
  .join('\n'))}
5478
5551
 
5479
- Last error ${((_b = $ongoingTaskResult.$expectError) === null || _b === void 0 ? void 0 : _b.name) || ''}:
5480
- ${block((((_c = $ongoingTaskResult.$expectError) === null || _c === void 0 ? void 0 : _c.message) || '')
5481
- .split('\n')
5482
- .map((line) => `> ${line}`)
5483
- .join('\n'))}
5484
-
5485
- Last result:
5486
- ${block($ongoingTaskResult.$resultString === null
5487
- ? 'null'
5488
- : spaceTrim.spaceTrim($ongoingTaskResult.$resultString)
5489
- .split('\n')
5490
- .map((line) => `> ${line}`)
5491
- .join('\n'))}
5492
- ---
5552
+ All Failed Attempts:
5553
+ ${block(failuresSummary)}
5493
5554
  `;
5494
5555
  }));
5495
5556
  }
@@ -6320,6 +6381,46 @@
6320
6381
  return pipelineExecutor;
6321
6382
  }
6322
6383
 
6384
+ /**
6385
+ * Detects if the code is running in a browser environment in main thread (Not in a web worker)
6386
+ *
6387
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
6388
+ *
6389
+ * @public exported from `@promptbook/utils`
6390
+ */
6391
+ const $isRunningInBrowser = new Function(`
6392
+ try {
6393
+ return this === window;
6394
+ } catch (e) {
6395
+ return false;
6396
+ }
6397
+ `);
6398
+ /**
6399
+ * TODO: [๐ŸŽบ]
6400
+ */
6401
+
6402
+ /**
6403
+ * Detects if the code is running in a web worker
6404
+ *
6405
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
6406
+ *
6407
+ * @public exported from `@promptbook/utils`
6408
+ */
6409
+ const $isRunningInWebWorker = new Function(`
6410
+ try {
6411
+ if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
6412
+ return true;
6413
+ } else {
6414
+ return false;
6415
+ }
6416
+ } catch (e) {
6417
+ return false;
6418
+ }
6419
+ `);
6420
+ /**
6421
+ * TODO: [๐ŸŽบ]
6422
+ */
6423
+
6323
6424
  /**
6324
6425
  * Register for LLM tools.
6325
6426
  *
@@ -6488,8 +6589,10 @@
6488
6589
  .list()
6489
6590
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
6490
6591
  if (registeredItem === undefined) {
6592
+ console.log('!!! $llmToolsRegister.list()', $llmToolsRegister.list());
6491
6593
  throw new Error(spaceTrim__default["default"]((block) => `
6492
6594
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
6595
+ Running in ${!$isRunningInBrowser() ? '' : 'browser environment'}${!$isRunningInNode() ? '' : 'node environment'}${!$isRunningInWebWorker() ? '' : 'worker environment'}
6493
6596
 
6494
6597
  You have probably forgotten install and import the provider package.
6495
6598
  To fix this issue, you can:
@@ -6607,24 +6710,6 @@
6607
6710
  * TODO: [๐ŸŒบ] Use some intermediate util splitWords
6608
6711
  */
6609
6712
 
6610
- /**
6611
- * Detects if the code is running in a browser environment in main thread (Not in a web worker)
6612
- *
6613
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
6614
- *
6615
- * @public exported from `@promptbook/utils`
6616
- */
6617
- new Function(`
6618
- try {
6619
- return this === window;
6620
- } catch (e) {
6621
- return false;
6622
- }
6623
- `);
6624
- /**
6625
- * TODO: [๐ŸŽบ]
6626
- */
6627
-
6628
6713
  /**
6629
6714
  * Detects if the code is running in jest environment
6630
6715
  *
@@ -6643,28 +6728,6 @@
6643
6728
  * TODO: [๐ŸŽบ]
6644
6729
  */
6645
6730
 
6646
- /**
6647
- * Detects if the code is running in a web worker
6648
- *
6649
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
6650
- *
6651
- * @public exported from `@promptbook/utils`
6652
- */
6653
- new Function(`
6654
- try {
6655
- if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
6656
- return true;
6657
- } else {
6658
- return false;
6659
- }
6660
- } catch (e) {
6661
- return false;
6662
- }
6663
- `);
6664
- /**
6665
- * TODO: [๐ŸŽบ]
6666
- */
6667
-
6668
6731
  /**
6669
6732
  * Makes first letter of a string uppercase
6670
6733
  *
@@ -7836,6 +7899,66 @@
7836
7899
  response.setHeader('X-Powered-By', 'Promptbook engine');
7837
7900
  next();
7838
7901
  });
7902
+ // Note: OpenAI-compatible chat completions endpoint
7903
+ app.post('/v1/chat/completions', async (request, response) => {
7904
+ // TODO: !!!! Make more promptbook-native:
7905
+ try {
7906
+ const params = request.body;
7907
+ const { model, messages } = params;
7908
+ // Convert messages to a single prompt
7909
+ const prompt = messages
7910
+ .map((message) => `${message.role}: ${message.content}`)
7911
+ .join('\n');
7912
+ // Get pipeline for the book
7913
+ if (!collection) {
7914
+ throw new Error('No collection available');
7915
+ }
7916
+ const pipeline = await collection.getPipelineByUrl(model);
7917
+ const pipelineExecutor = createPipelineExecutor({
7918
+ pipeline,
7919
+ tools: await getExecutionToolsFromIdentification({
7920
+ isAnonymous: true,
7921
+ llmToolsConfiguration: [],
7922
+ }),
7923
+ });
7924
+ // Execute the pipeline with the prompt content as input
7925
+ const result = await pipelineExecutor({ prompt }).asPromise({ isCrashedOnError: true });
7926
+ if (!result.isSuccessful) {
7927
+ throw new Error(`Failed to execute book: ${result.errors.join(', ')}`);
7928
+ }
7929
+ // Return the result in OpenAI-compatible format
7930
+ response.json({
7931
+ id: 'chatcmpl-' + Math.random().toString(36).substring(2),
7932
+ object: 'chat.completion',
7933
+ created: Math.floor(Date.now() / 1000),
7934
+ model,
7935
+ choices: [
7936
+ {
7937
+ index: 0,
7938
+ message: {
7939
+ role: 'assistant',
7940
+ content: result.outputParameters.response,
7941
+ },
7942
+ finish_reason: 'stop',
7943
+ },
7944
+ ],
7945
+ usage: {
7946
+ prompt_tokens: 0,
7947
+ completion_tokens: 0,
7948
+ total_tokens: 0,
7949
+ },
7950
+ });
7951
+ }
7952
+ catch (error) {
7953
+ response.status(500).json({
7954
+ error: {
7955
+ message: error instanceof Error ? error.message : 'Unknown error',
7956
+ type: 'server_error',
7957
+ code: 'internal_error',
7958
+ },
7959
+ });
7960
+ }
7961
+ });
7839
7962
  // TODO: [๐Ÿฅบ] Expose openapiJson to consumer and also allow to add new routes
7840
7963
  app.use(OpenApiValidator__namespace.middleware({
7841
7964
  apiSpec: openapiJson,
@@ -8196,7 +8319,6 @@
8196
8319
  catch (error) {
8197
8320
  assertsError(error);
8198
8321
  socket.emit('error', serializeError(error));
8199
- // <- TODO: [๐Ÿš‹] There is a problem with the remote server handling errors and sending them back to the client
8200
8322
  }
8201
8323
  finally {
8202
8324
  socket.disconnect();