@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/README.md CHANGED
@@ -25,6 +25,10 @@ Write AI applications using plain human language across multiple models and plat
25
25
 
26
26
 
27
27
 
28
+ <blockquote style="color: #ff8811">
29
+ <b>โš  Warning:</b> This is a pre-release version of the library. It is not yet ready for production use. Please look at <a href="https://www.npmjs.com/package/@promptbook/core?activeTab=versions">latest stable release</a>.
30
+ </blockquote>
31
+
28
32
  ## ๐Ÿ“ฆ Package `@promptbook/remote-server`
29
33
 
30
34
  - Promptbooks are [divided into several](#-packages) packages, all are published from [single monorepo](https://github.com/webgptorg/promptbook).
@@ -56,6 +60,8 @@ Rest of the documentation is common for **entire promptbook ecosystem**:
56
60
 
57
61
  During the computer revolution, we have seen [multiple generations of computer languages](https://github.com/webgptorg/promptbook/discussions/180), from the physical rewiring of the vacuum tubes through low-level machine code to the high-level languages like Python or JavaScript. And now, we're on the edge of the **next revolution**!
58
62
 
63
+
64
+
59
65
  It's a revolution of writing software in **plain human language** that is understandable and executable by both humans and machines โ€“ and it's going to change everything!
60
66
 
61
67
  The incredible growth in power of microprocessors and the Moore's Law have been the driving force behind the ever-more powerful languages, and it's been an amazing journey! Similarly, the large language models (like GPT or Claude) are the next big thing in language technology, and they're set to transform the way we interact with computers.
@@ -181,6 +187,8 @@ Join our growing community of developers and users:
181
187
 
182
188
  _A concise, Markdown-based DSL for crafting AI workflows and automations._
183
189
 
190
+
191
+
184
192
  ### Introduction
185
193
 
186
194
  Book is a Markdown-based language that simplifies the creation of AI applications, workflows, and automations. With human-readable commands, you can define inputs, outputs, personas, knowledge sources, and actionsโ€”without needing model-specific details.
@@ -230,6 +238,8 @@ Personas can have access to different knowledge, tools and actions. They can als
230
238
 
231
239
  - [PERSONA](https://github.com/webgptorg/promptbook/blob/main/documents/commands/PERSONA.md)
232
240
 
241
+
242
+
233
243
  ### **3. How:** Knowledge, Instruments and Actions
234
244
 
235
245
  The resources used by the personas are used to do the work.
@@ -329,6 +339,8 @@ The following glossary is used to clarify certain concepts:
329
339
 
330
340
  _Note: This section is not a complete dictionary, more list of general AI / LLM terms that has connection with Promptbook_
331
341
 
342
+
343
+
332
344
  ### ๐Ÿ’ฏ Core concepts
333
345
 
334
346
  - [๐Ÿ“š Collection of pipelines](https://github.com/webgptorg/promptbook/discussions/65)
package/esm/index.es.js CHANGED
@@ -33,7 +33,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
33
33
  * @generated
34
34
  * @see https://github.com/webgptorg/promptbook
35
35
  */
36
- const PROMPTBOOK_ENGINE_VERSION = '0.95.0';
36
+ const PROMPTBOOK_ENGINE_VERSION = '0.98.0-10';
37
37
  /**
38
38
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
39
39
  * Note: [๐Ÿ’ž] Ignore a discrepancy between file name and entity name
@@ -173,7 +173,7 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
173
173
  *
174
174
  * @public exported from `@promptbook/core`
175
175
  */
176
- const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
176
+ const DEFAULT_MAX_EXECUTION_ATTEMPTS = 7; // <- TODO: [๐Ÿคนโ€โ™‚๏ธ]
177
177
  // <- TODO: [๐Ÿ•] Make also `BOOKS_DIRNAME_ALTERNATIVES`
178
178
  // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
179
179
  /**
@@ -1891,7 +1891,7 @@ function jsonParse(value) {
1891
1891
  throw new Error(spaceTrim((block) => `
1892
1892
  ${block(error.message)}
1893
1893
 
1894
- The JSON text:
1894
+ The expected JSON text:
1895
1895
  ${block(value)}
1896
1896
  `));
1897
1897
  }
@@ -4813,6 +4813,94 @@ function mapAvailableToExpectedParameters(options) {
4813
4813
  return mappedParameters;
4814
4814
  }
4815
4815
 
4816
+ /**
4817
+ * Just says that the variable is not used but should be kept
4818
+ * No side effects.
4819
+ *
4820
+ * Note: It can be useful for:
4821
+ *
4822
+ * 1) Suppressing eager optimization of unused imports
4823
+ * 2) Suppressing eslint errors of unused variables in the tests
4824
+ * 3) Keeping the type of the variable for type testing
4825
+ *
4826
+ * @param value any values
4827
+ * @returns void
4828
+ * @private within the repository
4829
+ */
4830
+ function keepUnused(...valuesToKeep) {
4831
+ }
4832
+
4833
+ /**
4834
+ * Replaces parameters in template with values from parameters object
4835
+ *
4836
+ * Note: This function is not places strings into string,
4837
+ * It's more complex and can handle this operation specifically for LLM models
4838
+ *
4839
+ * @param template the template with parameters in {curly} braces
4840
+ * @param parameters the object with parameters
4841
+ * @returns the template with replaced parameters
4842
+ * @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
4843
+ * @public exported from `@promptbook/utils`
4844
+ */
4845
+ function templateParameters(template, parameters) {
4846
+ for (const [parameterName, parameterValue] of Object.entries(parameters)) {
4847
+ if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
4848
+ throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
4849
+ }
4850
+ else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
4851
+ // TODO: [๐Ÿต]
4852
+ throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
4853
+ }
4854
+ }
4855
+ let replacedTemplates = template;
4856
+ let match;
4857
+ let loopLimit = LOOP_LIMIT;
4858
+ while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
4859
+ .exec(replacedTemplates))) {
4860
+ if (loopLimit-- < 0) {
4861
+ throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
4862
+ }
4863
+ const precol = match.groups.precol;
4864
+ const parameterName = match.groups.parameterName;
4865
+ if (parameterName === '') {
4866
+ // Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
4867
+ continue;
4868
+ }
4869
+ if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
4870
+ throw new PipelineExecutionError('Parameter is already opened or not closed');
4871
+ }
4872
+ if (parameters[parameterName] === undefined) {
4873
+ throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4874
+ }
4875
+ let parameterValue = parameters[parameterName];
4876
+ if (parameterValue === undefined) {
4877
+ throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4878
+ }
4879
+ parameterValue = valueToString(parameterValue);
4880
+ // Escape curly braces in parameter values to prevent prompt-injection
4881
+ parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4882
+ if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
4883
+ parameterValue = parameterValue
4884
+ .split('\n')
4885
+ .map((line, index) => (index === 0 ? line : `${precol}${line}`))
4886
+ .join('\n');
4887
+ }
4888
+ replacedTemplates =
4889
+ replacedTemplates.substring(0, match.index + precol.length) +
4890
+ parameterValue +
4891
+ replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
4892
+ }
4893
+ // [๐Ÿ’ซ] Check if there are parameters that are not closed properly
4894
+ if (/{\w+$/.test(replacedTemplates)) {
4895
+ throw new PipelineExecutionError('Parameter is not closed');
4896
+ }
4897
+ // [๐Ÿ’ซ] Check if there are parameters that are not opened properly
4898
+ if (/^\w+}/.test(replacedTemplates)) {
4899
+ throw new PipelineExecutionError('Parameter is not opened');
4900
+ }
4901
+ return replacedTemplates;
4902
+ }
4903
+
4816
4904
  /**
4817
4905
  * Extracts all code blocks from markdown.
4818
4906
  *
@@ -4915,94 +5003,6 @@ function extractJsonBlock(markdown) {
4915
5003
  * TODO: [๐Ÿข] Make this logic part of `JsonFormatParser` or `isValidJsonString`
4916
5004
  */
4917
5005
 
4918
- /**
4919
- * Just says that the variable is not used but should be kept
4920
- * No side effects.
4921
- *
4922
- * Note: It can be useful for:
4923
- *
4924
- * 1) Suppressing eager optimization of unused imports
4925
- * 2) Suppressing eslint errors of unused variables in the tests
4926
- * 3) Keeping the type of the variable for type testing
4927
- *
4928
- * @param value any values
4929
- * @returns void
4930
- * @private within the repository
4931
- */
4932
- function keepUnused(...valuesToKeep) {
4933
- }
4934
-
4935
- /**
4936
- * Replaces parameters in template with values from parameters object
4937
- *
4938
- * Note: This function is not places strings into string,
4939
- * It's more complex and can handle this operation specifically for LLM models
4940
- *
4941
- * @param template the template with parameters in {curly} braces
4942
- * @param parameters the object with parameters
4943
- * @returns the template with replaced parameters
4944
- * @throws {PipelineExecutionError} if parameter is not defined, not closed, or not opened
4945
- * @public exported from `@promptbook/utils`
4946
- */
4947
- function templateParameters(template, parameters) {
4948
- for (const [parameterName, parameterValue] of Object.entries(parameters)) {
4949
- if (parameterValue === RESERVED_PARAMETER_MISSING_VALUE) {
4950
- throw new UnexpectedError(`Parameter \`{${parameterName}}\` has missing value`);
4951
- }
4952
- else if (parameterValue === RESERVED_PARAMETER_RESTRICTED) {
4953
- // TODO: [๐Ÿต]
4954
- throw new UnexpectedError(`Parameter \`{${parameterName}}\` is restricted to use`);
4955
- }
4956
- }
4957
- let replacedTemplates = template;
4958
- let match;
4959
- let loopLimit = LOOP_LIMIT;
4960
- while ((match = /^(?<precol>.*){(?<parameterName>\w+)}(.*)/m /* <- Not global */
4961
- .exec(replacedTemplates))) {
4962
- if (loopLimit-- < 0) {
4963
- throw new LimitReachedError('Loop limit reached during parameters replacement in `templateParameters`');
4964
- }
4965
- const precol = match.groups.precol;
4966
- const parameterName = match.groups.parameterName;
4967
- if (parameterName === '') {
4968
- // Note: Skip empty placeholders. It's used to avoid confusion with JSON-like strings
4969
- continue;
4970
- }
4971
- if (parameterName.indexOf('{') !== -1 || parameterName.indexOf('}') !== -1) {
4972
- throw new PipelineExecutionError('Parameter is already opened or not closed');
4973
- }
4974
- if (parameters[parameterName] === undefined) {
4975
- throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4976
- }
4977
- let parameterValue = parameters[parameterName];
4978
- if (parameterValue === undefined) {
4979
- throw new PipelineExecutionError(`Parameter \`{${parameterName}}\` is not defined`);
4980
- }
4981
- parameterValue = valueToString(parameterValue);
4982
- // Escape curly braces in parameter values to prevent prompt-injection
4983
- parameterValue = parameterValue.replace(/[{}]/g, '\\$&');
4984
- if (parameterValue.includes('\n') && /^\s*\W{0,3}\s*$/.test(precol)) {
4985
- parameterValue = parameterValue
4986
- .split('\n')
4987
- .map((line, index) => (index === 0 ? line : `${precol}${line}`))
4988
- .join('\n');
4989
- }
4990
- replacedTemplates =
4991
- replacedTemplates.substring(0, match.index + precol.length) +
4992
- parameterValue +
4993
- replacedTemplates.substring(match.index + precol.length + parameterName.length + 2);
4994
- }
4995
- // [๐Ÿ’ซ] Check if there are parameters that are not closed properly
4996
- if (/{\w+$/.test(replacedTemplates)) {
4997
- throw new PipelineExecutionError('Parameter is not closed');
4998
- }
4999
- // [๐Ÿ’ซ] Check if there are parameters that are not opened properly
5000
- if (/^\w+}/.test(replacedTemplates)) {
5001
- throw new PipelineExecutionError('Parameter is not opened');
5002
- }
5003
- return replacedTemplates;
5004
- }
5005
-
5006
5006
  /**
5007
5007
  * Counts number of characters in the text
5008
5008
  *
@@ -5163,6 +5163,68 @@ function checkExpectations(expectations, value) {
5163
5163
  * Note: [๐Ÿ’] and [๐Ÿค ] are interconnected together
5164
5164
  */
5165
5165
 
5166
+ /**
5167
+ * Validates a prompt result against expectations and format requirements.
5168
+ * This function provides a common abstraction for result validation that can be used
5169
+ * by both execution logic and caching logic to ensure consistency.
5170
+ *
5171
+ * @param options - The validation options including result string, expectations, and format
5172
+ * @returns Validation result with processed string and validity status
5173
+ * @private internal function of `createPipelineExecutor` and `cacheLlmTools`
5174
+ */
5175
+ function validatePromptResult(options) {
5176
+ const { resultString, expectations, format } = options;
5177
+ let processedResultString = resultString;
5178
+ let validationError;
5179
+ try {
5180
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
5181
+ if (format) {
5182
+ if (format === 'JSON') {
5183
+ if (!isValidJsonString(processedResultString)) {
5184
+ // TODO: [๐Ÿข] Do more universally via `FormatParser`
5185
+ try {
5186
+ processedResultString = extractJsonBlock(processedResultString);
5187
+ }
5188
+ catch (error) {
5189
+ keepUnused(error);
5190
+ throw new ExpectError(spaceTrim$1((block) => `
5191
+ Expected valid JSON string
5192
+
5193
+ The expected JSON text:
5194
+ ${block(processedResultString)}
5195
+ `));
5196
+ }
5197
+ }
5198
+ }
5199
+ else {
5200
+ throw new UnexpectedError(`Unknown format "${format}"`);
5201
+ }
5202
+ }
5203
+ // TODO: [๐Ÿ’] Unite object for expecting amount and format
5204
+ if (expectations) {
5205
+ checkExpectations(expectations, processedResultString);
5206
+ }
5207
+ return {
5208
+ isValid: true,
5209
+ processedResultString,
5210
+ };
5211
+ }
5212
+ catch (error) {
5213
+ if (error instanceof ExpectError) {
5214
+ validationError = error;
5215
+ }
5216
+ else {
5217
+ // Re-throw non-ExpectError errors (like UnexpectedError)
5218
+ throw error;
5219
+ }
5220
+ return {
5221
+ isValid: false,
5222
+ processedResultString,
5223
+ error: validationError,
5224
+ };
5225
+ }
5226
+ }
5227
+
5166
5228
  /**
5167
5229
  * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
5168
5230
  * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
@@ -5180,17 +5242,18 @@ async function executeAttempts(options) {
5180
5242
  $resultString: null,
5181
5243
  $expectError: null,
5182
5244
  $scriptPipelineExecutionErrors: [],
5245
+ $failedResults: [], // Track all failed attempts
5183
5246
  };
5184
5247
  // TODO: [๐Ÿš] Make arrayable LLMs -> single LLM DRY
5185
5248
  const _llms = arrayableToArray(tools.llm);
5186
5249
  const llmTools = _llms.length === 1 ? _llms[0] : joinLlmExecutionTools(..._llms);
5187
- attempts: for (let attempt = -jokerParameterNames.length; attempt < maxAttempts; attempt++) {
5188
- const isJokerAttempt = attempt < 0;
5189
- const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
5250
+ attempts: for (let attemptIndex = -jokerParameterNames.length; attemptIndex < maxAttempts; attemptIndex++) {
5251
+ const isJokerAttempt = attemptIndex < 0;
5252
+ const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attemptIndex];
5190
5253
  // TODO: [๐Ÿง ][๐Ÿญ] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
5191
5254
  if (isJokerAttempt && !jokerParameterName) {
5192
5255
  throw new UnexpectedError(spaceTrim$1((block) => `
5193
- Joker not found in attempt ${attempt}
5256
+ Joker not found in attempt ${attemptIndex}
5194
5257
 
5195
5258
  ${block(pipelineIdentification)}
5196
5259
  `));
@@ -5388,35 +5451,18 @@ async function executeAttempts(options) {
5388
5451
  }
5389
5452
  }
5390
5453
  // TODO: [๐Ÿ’] Unite object for expecting amount and format
5391
- if (task.format) {
5392
- if (task.format === 'JSON') {
5393
- if (!isValidJsonString($ongoingTaskResult.$resultString || '')) {
5394
- // TODO: [๐Ÿข] Do more universally via `FormatParser`
5395
- try {
5396
- $ongoingTaskResult.$resultString = extractJsonBlock($ongoingTaskResult.$resultString || '');
5397
- }
5398
- catch (error) {
5399
- keepUnused(error);
5400
- throw new ExpectError(spaceTrim$1((block) => `
5401
- Expected valid JSON string
5402
-
5403
- ${block(
5404
- /*<- Note: No need for `pipelineIdentification`, it will be catched and added later */ '')}
5405
- `));
5406
- }
5407
- }
5408
- }
5409
- else {
5410
- throw new UnexpectedError(spaceTrim$1((block) => `
5411
- Unknown format "${task.format}"
5412
-
5413
- ${block(pipelineIdentification)}
5414
- `));
5454
+ // Use the common validation function for both format and expectations
5455
+ if (task.format || task.expectations) {
5456
+ const validationResult = validatePromptResult({
5457
+ resultString: $ongoingTaskResult.$resultString || '',
5458
+ expectations: task.expectations,
5459
+ format: task.format,
5460
+ });
5461
+ if (!validationResult.isValid) {
5462
+ throw validationResult.error;
5415
5463
  }
5416
- }
5417
- // TODO: [๐Ÿ’] Unite object for expecting amount and format
5418
- if (task.expectations) {
5419
- checkExpectations(task.expectations, $ongoingTaskResult.$resultString || '');
5464
+ // Update the result string in case format processing modified it (e.g., JSON extraction)
5465
+ $ongoingTaskResult.$resultString = validationResult.processedResultString;
5420
5466
  }
5421
5467
  break attempts;
5422
5468
  }
@@ -5425,6 +5471,15 @@ async function executeAttempts(options) {
5425
5471
  throw error;
5426
5472
  }
5427
5473
  $ongoingTaskResult.$expectError = error;
5474
+ // Store each failed attempt
5475
+ if (!Array.isArray($ongoingTaskResult.$failedResults)) {
5476
+ $ongoingTaskResult.$failedResults = [];
5477
+ }
5478
+ $ongoingTaskResult.$failedResults.push({
5479
+ attemptIndex,
5480
+ result: $ongoingTaskResult.$resultString,
5481
+ error: error,
5482
+ });
5428
5483
  }
5429
5484
  finally {
5430
5485
  if (!isJokerAttempt &&
@@ -5446,35 +5501,41 @@ async function executeAttempts(options) {
5446
5501
  });
5447
5502
  }
5448
5503
  }
5449
- if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
5504
+ if ($ongoingTaskResult.$expectError !== null && attemptIndex === maxAttempts - 1) {
5505
+ // Note: Create a summary of all failures
5506
+ const failuresSummary = $ongoingTaskResult.$failedResults
5507
+ .map((failure) => spaceTrim$1((block) => {
5508
+ var _a, _b;
5509
+ return `
5510
+ Attempt ${failure.attemptIndex + 1}:
5511
+ Error ${((_a = failure.error) === null || _a === void 0 ? void 0 : _a.name) || ''}:
5512
+ ${block((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message.split('\n').map((line) => `> ${line}`).join('\n'))}
5513
+
5514
+ Result:
5515
+ ${block(failure.result === null
5516
+ ? 'null'
5517
+ : spaceTrim$1(failure.result)
5518
+ .split('\n')
5519
+ .map((line) => `> ${line}`)
5520
+ .join('\n'))}
5521
+ `;
5522
+ }))
5523
+ .join('\n\n---\n\n');
5450
5524
  throw new PipelineExecutionError(spaceTrim$1((block) => {
5451
- var _a, _b, _c;
5525
+ var _a;
5452
5526
  return `
5453
5527
  LLM execution failed ${maxExecutionAttempts}x
5454
5528
 
5455
5529
  ${block(pipelineIdentification)}
5456
5530
 
5457
- ---
5458
5531
  The Prompt:
5459
5532
  ${block((((_a = $ongoingTaskResult.$prompt) === null || _a === void 0 ? void 0 : _a.content) || '')
5460
5533
  .split('\n')
5461
5534
  .map((line) => `> ${line}`)
5462
5535
  .join('\n'))}
5463
5536
 
5464
- Last error ${((_b = $ongoingTaskResult.$expectError) === null || _b === void 0 ? void 0 : _b.name) || ''}:
5465
- ${block((((_c = $ongoingTaskResult.$expectError) === null || _c === void 0 ? void 0 : _c.message) || '')
5466
- .split('\n')
5467
- .map((line) => `> ${line}`)
5468
- .join('\n'))}
5469
-
5470
- Last result:
5471
- ${block($ongoingTaskResult.$resultString === null
5472
- ? 'null'
5473
- : spaceTrim$1($ongoingTaskResult.$resultString)
5474
- .split('\n')
5475
- .map((line) => `> ${line}`)
5476
- .join('\n'))}
5477
- ---
5537
+ All Failed Attempts:
5538
+ ${block(failuresSummary)}
5478
5539
  `;
5479
5540
  }));
5480
5541
  }
@@ -6305,6 +6366,46 @@ function createPipelineExecutor(options) {
6305
6366
  return pipelineExecutor;
6306
6367
  }
6307
6368
 
6369
+ /**
6370
+ * Detects if the code is running in a browser environment in main thread (Not in a web worker)
6371
+ *
6372
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
6373
+ *
6374
+ * @public exported from `@promptbook/utils`
6375
+ */
6376
+ const $isRunningInBrowser = new Function(`
6377
+ try {
6378
+ return this === window;
6379
+ } catch (e) {
6380
+ return false;
6381
+ }
6382
+ `);
6383
+ /**
6384
+ * TODO: [๐ŸŽบ]
6385
+ */
6386
+
6387
+ /**
6388
+ * Detects if the code is running in a web worker
6389
+ *
6390
+ * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
6391
+ *
6392
+ * @public exported from `@promptbook/utils`
6393
+ */
6394
+ const $isRunningInWebWorker = new Function(`
6395
+ try {
6396
+ if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
6397
+ return true;
6398
+ } else {
6399
+ return false;
6400
+ }
6401
+ } catch (e) {
6402
+ return false;
6403
+ }
6404
+ `);
6405
+ /**
6406
+ * TODO: [๐ŸŽบ]
6407
+ */
6408
+
6308
6409
  /**
6309
6410
  * Register for LLM tools.
6310
6411
  *
@@ -6473,8 +6574,10 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
6473
6574
  .list()
6474
6575
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
6475
6576
  if (registeredItem === undefined) {
6577
+ console.log('!!! $llmToolsRegister.list()', $llmToolsRegister.list());
6476
6578
  throw new Error(spaceTrim((block) => `
6477
6579
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
6580
+ Running in ${!$isRunningInBrowser() ? '' : 'browser environment'}${!$isRunningInNode() ? '' : 'node environment'}${!$isRunningInWebWorker() ? '' : 'worker environment'}
6478
6581
 
6479
6582
  You have probably forgotten install and import the provider package.
6480
6583
  To fix this issue, you can:
@@ -6592,24 +6695,6 @@ function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
6592
6695
  * TODO: [๐ŸŒบ] Use some intermediate util splitWords
6593
6696
  */
6594
6697
 
6595
- /**
6596
- * Detects if the code is running in a browser environment in main thread (Not in a web worker)
6597
- *
6598
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
6599
- *
6600
- * @public exported from `@promptbook/utils`
6601
- */
6602
- new Function(`
6603
- try {
6604
- return this === window;
6605
- } catch (e) {
6606
- return false;
6607
- }
6608
- `);
6609
- /**
6610
- * TODO: [๐ŸŽบ]
6611
- */
6612
-
6613
6698
  /**
6614
6699
  * Detects if the code is running in jest environment
6615
6700
  *
@@ -6628,28 +6713,6 @@ new Function(`
6628
6713
  * TODO: [๐ŸŽบ]
6629
6714
  */
6630
6715
 
6631
- /**
6632
- * Detects if the code is running in a web worker
6633
- *
6634
- * Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
6635
- *
6636
- * @public exported from `@promptbook/utils`
6637
- */
6638
- new Function(`
6639
- try {
6640
- if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
6641
- return true;
6642
- } else {
6643
- return false;
6644
- }
6645
- } catch (e) {
6646
- return false;
6647
- }
6648
- `);
6649
- /**
6650
- * TODO: [๐ŸŽบ]
6651
- */
6652
-
6653
6716
  /**
6654
6717
  * Makes first letter of a string uppercase
6655
6718
  *
@@ -7821,6 +7884,66 @@ function startRemoteServer(options) {
7821
7884
  response.setHeader('X-Powered-By', 'Promptbook engine');
7822
7885
  next();
7823
7886
  });
7887
+ // Note: OpenAI-compatible chat completions endpoint
7888
+ app.post('/v1/chat/completions', async (request, response) => {
7889
+ // TODO: !!!! Make more promptbook-native:
7890
+ try {
7891
+ const params = request.body;
7892
+ const { model, messages } = params;
7893
+ // Convert messages to a single prompt
7894
+ const prompt = messages
7895
+ .map((message) => `${message.role}: ${message.content}`)
7896
+ .join('\n');
7897
+ // Get pipeline for the book
7898
+ if (!collection) {
7899
+ throw new Error('No collection available');
7900
+ }
7901
+ const pipeline = await collection.getPipelineByUrl(model);
7902
+ const pipelineExecutor = createPipelineExecutor({
7903
+ pipeline,
7904
+ tools: await getExecutionToolsFromIdentification({
7905
+ isAnonymous: true,
7906
+ llmToolsConfiguration: [],
7907
+ }),
7908
+ });
7909
+ // Execute the pipeline with the prompt content as input
7910
+ const result = await pipelineExecutor({ prompt }).asPromise({ isCrashedOnError: true });
7911
+ if (!result.isSuccessful) {
7912
+ throw new Error(`Failed to execute book: ${result.errors.join(', ')}`);
7913
+ }
7914
+ // Return the result in OpenAI-compatible format
7915
+ response.json({
7916
+ id: 'chatcmpl-' + Math.random().toString(36).substring(2),
7917
+ object: 'chat.completion',
7918
+ created: Math.floor(Date.now() / 1000),
7919
+ model,
7920
+ choices: [
7921
+ {
7922
+ index: 0,
7923
+ message: {
7924
+ role: 'assistant',
7925
+ content: result.outputParameters.response,
7926
+ },
7927
+ finish_reason: 'stop',
7928
+ },
7929
+ ],
7930
+ usage: {
7931
+ prompt_tokens: 0,
7932
+ completion_tokens: 0,
7933
+ total_tokens: 0,
7934
+ },
7935
+ });
7936
+ }
7937
+ catch (error) {
7938
+ response.status(500).json({
7939
+ error: {
7940
+ message: error instanceof Error ? error.message : 'Unknown error',
7941
+ type: 'server_error',
7942
+ code: 'internal_error',
7943
+ },
7944
+ });
7945
+ }
7946
+ });
7824
7947
  // TODO: [๐Ÿฅบ] Expose openapiJson to consumer and also allow to add new routes
7825
7948
  app.use(OpenApiValidator.middleware({
7826
7949
  apiSpec: openapiJson,
@@ -8181,7 +8304,6 @@ function startRemoteServer(options) {
8181
8304
  catch (error) {
8182
8305
  assertsError(error);
8183
8306
  socket.emit('error', serializeError(error));
8184
- // <- TODO: [๐Ÿš‹] There is a problem with the remote server handling errors and sending them back to the client
8185
8307
  }
8186
8308
  finally {
8187
8309
  socket.disconnect();