@promptbook/cli 0.104.0 → 0.105.0-0

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/README.md CHANGED
@@ -27,6 +27,10 @@ Turn your company's scattered knowledge into AI ready Books
27
27
 
28
28
 
29
29
 
30
+ <blockquote style="color: #ff8811">
31
+ <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>.
32
+ </blockquote>
33
+
30
34
  ## 📦 Package `@promptbook/cli`
31
35
 
32
36
  - Promptbooks are [divided into several](#-packages) packages, all are published from [single monorepo](https://github.com/webgptorg/promptbook).
package/esm/index.es.js CHANGED
@@ -47,7 +47,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
47
47
  * @generated
48
48
  * @see https://github.com/webgptorg/promptbook
49
49
  */
50
- const PROMPTBOOK_ENGINE_VERSION = '0.104.0';
50
+ const PROMPTBOOK_ENGINE_VERSION = '0.105.0-0';
51
51
  /**
52
52
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
53
53
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3493,7 +3493,7 @@ const ALL_ERRORS = {
3493
3493
  *
3494
3494
  * @public exported from `@promptbook/utils`
3495
3495
  */
3496
- function deserializeError(error) {
3496
+ function deserializeError(error, isStackAddedToMessage = true) {
3497
3497
  const { name, stack, id } = error; // Added id
3498
3498
  let { message } = error;
3499
3499
  let ErrorClass = ALL_ERRORS[error.name];
@@ -3501,7 +3501,7 @@ function deserializeError(error) {
3501
3501
  ErrorClass = Error;
3502
3502
  message = `${name}: ${message}`;
3503
3503
  }
3504
- if (stack !== undefined && stack !== '') {
3504
+ if (isStackAddedToMessage && stack !== undefined && stack !== '') {
3505
3505
  message = spaceTrim$2((block) => `
3506
3506
  ${block(message)}
3507
3507
 
@@ -4303,6 +4303,9 @@ function computeHash(value) {
4303
4303
  * @public exported from `@promptbook/utils`
4304
4304
  */
4305
4305
  function parseNumber(value) {
4306
+ if (value === null || value === undefined) {
4307
+ return 0;
4308
+ }
4306
4309
  const originalValue = value;
4307
4310
  if (typeof value === 'number') {
4308
4311
  value = value.toString(); // <- TODO: Maybe more efficient way to do this
@@ -19495,83 +19498,180 @@ class OpenAiCompatibleExecutionTools {
19495
19498
  content: msg.content,
19496
19499
  }));
19497
19500
  }
19498
- const rawRequest = {
19499
- ...modelSettings,
19500
- messages: [
19501
- ...(currentModelRequirements.systemMessage === undefined
19502
- ? []
19503
- : [
19504
- {
19505
- role: 'system',
19506
- content: currentModelRequirements.systemMessage,
19507
- },
19508
- ]),
19509
- ...threadMessages,
19510
- {
19511
- role: 'user',
19512
- content: rawPromptContent,
19513
- },
19514
- ],
19515
- user: (_a = this.options.userId) === null || _a === void 0 ? void 0 : _a.toString(),
19516
- tools: currentModelRequirements.tools === undefined
19517
- ? undefined
19518
- : mapToolsToOpenAi(currentModelRequirements.tools),
19501
+ const messages = [
19502
+ ...(currentModelRequirements.systemMessage === undefined
19503
+ ? []
19504
+ : [
19505
+ {
19506
+ role: 'system',
19507
+ content: currentModelRequirements.systemMessage,
19508
+ },
19509
+ ]),
19510
+ ...threadMessages,
19511
+ {
19512
+ role: 'user',
19513
+ content: rawPromptContent,
19514
+ },
19515
+ ];
19516
+ let totalUsage = {
19517
+ price: uncertainNumber(0),
19518
+ input: {
19519
+ tokensCount: uncertainNumber(0),
19520
+ charactersCount: uncertainNumber(0),
19521
+ wordsCount: uncertainNumber(0),
19522
+ sentencesCount: uncertainNumber(0),
19523
+ linesCount: uncertainNumber(0),
19524
+ paragraphsCount: uncertainNumber(0),
19525
+ pagesCount: uncertainNumber(0),
19526
+ },
19527
+ output: {
19528
+ tokensCount: uncertainNumber(0),
19529
+ charactersCount: uncertainNumber(0),
19530
+ wordsCount: uncertainNumber(0),
19531
+ sentencesCount: uncertainNumber(0),
19532
+ linesCount: uncertainNumber(0),
19533
+ paragraphsCount: uncertainNumber(0),
19534
+ pagesCount: uncertainNumber(0),
19535
+ },
19519
19536
  };
19537
+ const toolCalls = [];
19520
19538
  const start = $getCurrentDate();
19521
- if (this.options.isVerbose) {
19522
- console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
19523
- }
19524
- try {
19525
- const rawResponse = await this.limiter
19526
- .schedule(() => this.makeRequestWithNetworkRetry(() => client.chat.completions.create(rawRequest)))
19527
- .catch((error) => {
19528
- assertsError(error);
19529
- if (this.options.isVerbose) {
19530
- console.info(colors.bgRed('error'), error);
19531
- }
19532
- throw error;
19533
- });
19539
+ const tools = 'tools' in prompt && Array.isArray(prompt.tools) ? prompt.tools : currentModelRequirements.tools;
19540
+ let isLooping = true;
19541
+ while (isLooping) {
19542
+ const rawRequest = {
19543
+ ...modelSettings,
19544
+ messages,
19545
+ user: (_a = this.options.userId) === null || _a === void 0 ? void 0 : _a.toString(),
19546
+ tools: tools === undefined ? undefined : mapToolsToOpenAi(tools),
19547
+ };
19534
19548
  if (this.options.isVerbose) {
19535
- console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
19536
- }
19537
- const complete = $getCurrentDate();
19538
- if (!rawResponse.choices[0]) {
19539
- throw new PipelineExecutionError(`No choises from ${this.title}`);
19540
- }
19541
- if (rawResponse.choices.length > 1) {
19542
- // TODO: This should be maybe only warning
19543
- throw new PipelineExecutionError(`More than one choise from ${this.title}`);
19544
- }
19545
- const resultContent = rawResponse.choices[0].message.content;
19546
- const usage = this.computeUsage(content || '', resultContent || '', rawResponse);
19547
- if (resultContent === null) {
19548
- throw new PipelineExecutionError(`No response message from ${this.title}`);
19549
+ console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
19549
19550
  }
19550
- return exportJson({
19551
- name: 'promptResult',
19552
- message: `Result of \`OpenAiCompatibleExecutionTools.callChatModel\``,
19553
- order: [],
19554
- value: {
19555
- content: resultContent,
19556
- modelName: rawResponse.model || modelName,
19557
- timing: {
19558
- start,
19559
- complete,
19551
+ try {
19552
+ const rawResponse = await this.limiter
19553
+ .schedule(() => this.makeRequestWithNetworkRetry(() => client.chat.completions.create(rawRequest)))
19554
+ .catch((error) => {
19555
+ assertsError(error);
19556
+ if (this.options.isVerbose) {
19557
+ console.info(colors.bgRed('error'), error);
19558
+ }
19559
+ throw error;
19560
+ });
19561
+ if (this.options.isVerbose) {
19562
+ console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
19563
+ }
19564
+ if (!rawResponse.choices[0]) {
19565
+ throw new PipelineExecutionError(`No choises from ${this.title}`);
19566
+ }
19567
+ const responseMessage = rawResponse.choices[0].message;
19568
+ messages.push(responseMessage);
19569
+ const usage = this.computeUsage(content || '', responseMessage.content || '', rawResponse);
19570
+ totalUsage = addUsage(totalUsage, usage);
19571
+ if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
19572
+ await forEachAsync(responseMessage.tool_calls, {}, async (toolCall) => {
19573
+ const functionName = toolCall.function.name;
19574
+ const functionArgs = toolCall.function.arguments;
19575
+ const executionTools = this.options
19576
+ .executionTools;
19577
+ if (!executionTools || !executionTools.script) {
19578
+ throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiCompatibleExecutionTools options`);
19579
+ }
19580
+ // TODO: [DRY] Use some common tool caller
19581
+ const scriptTools = Array.isArray(executionTools.script)
19582
+ ? executionTools.script
19583
+ : [executionTools.script];
19584
+ let functionResponse;
19585
+ try {
19586
+ const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
19587
+ functionResponse = await scriptTool.execute({
19588
+ scriptLanguage: 'javascript',
19589
+ script: `
19590
+ const args = ${functionArgs};
19591
+ return await ${functionName}(args);
19592
+ `,
19593
+ parameters: {}, // <- TODO: [🧠] What parameters to pass?
19594
+ });
19595
+ }
19596
+ catch (error) {
19597
+ assertsError(error);
19598
+ functionResponse = `Error: ${error.message}`;
19599
+ }
19600
+ messages.push({
19601
+ role: 'tool',
19602
+ tool_call_id: toolCall.id,
19603
+ content: functionResponse,
19604
+ });
19605
+ toolCalls.push({
19606
+ name: functionName,
19607
+ arguments: functionArgs,
19608
+ result: functionResponse,
19609
+ rawToolCall: toolCall,
19610
+ });
19611
+ });
19612
+ continue;
19613
+ }
19614
+ const complete = $getCurrentDate();
19615
+ const resultContent = responseMessage.content;
19616
+ if (resultContent === null) {
19617
+ throw new PipelineExecutionError(`No response message from ${this.title}`);
19618
+ }
19619
+ isLooping = false;
19620
+ return exportJson({
19621
+ name: 'promptResult',
19622
+ message: `Result of \`OpenAiCompatibleExecutionTools.callChatModel\``,
19623
+ order: [],
19624
+ value: {
19625
+ content: resultContent,
19626
+ modelName: rawResponse.model || modelName,
19627
+ timing: {
19628
+ start,
19629
+ complete,
19630
+ },
19631
+ usage: totalUsage,
19632
+ toolCalls,
19633
+ rawPromptContent,
19634
+ rawRequest,
19635
+ rawResponse,
19560
19636
  },
19561
- usage,
19562
- rawPromptContent,
19563
- rawRequest,
19564
- rawResponse,
19565
- // <- [🗯]
19566
- },
19567
- });
19568
- }
19569
- catch (error) {
19570
- assertsError(error);
19571
- // Check if this is an unsupported parameter error
19572
- if (!isUnsupportedParameterError(error)) {
19573
- // If we have attemptStack, include it in the error message
19574
- if (attemptStack.length > 0) {
19637
+ });
19638
+ }
19639
+ catch (error) {
19640
+ isLooping = false;
19641
+ assertsError(error);
19642
+ // Check if this is an unsupported parameter error
19643
+ if (!isUnsupportedParameterError(error)) {
19644
+ // If we have attemptStack, include it in the error message
19645
+ if (attemptStack.length > 0) {
19646
+ throw new PipelineExecutionError(`All attempts failed. Attempt history:\n` +
19647
+ attemptStack
19648
+ .map((a, i) => ` ${i + 1}. Model: ${a.modelName}` +
19649
+ (a.unsupportedParameter ? `, Stripped: ${a.unsupportedParameter}` : '') +
19650
+ `, Error: ${a.errorMessage}` +
19651
+ (a.stripped ? ' (stripped and retried)' : ''))
19652
+ .join('\n') +
19653
+ `\nFinal error: ${error.message}`);
19654
+ }
19655
+ throw error;
19656
+ }
19657
+ // Parse which parameter is unsupported
19658
+ const unsupportedParameter = parseUnsupportedParameterError(error.message);
19659
+ if (!unsupportedParameter) {
19660
+ if (this.options.isVerbose) {
19661
+ console.warn(colors.bgYellow('Warning'), 'Could not parse unsupported parameter from error:', error.message);
19662
+ }
19663
+ throw error;
19664
+ }
19665
+ // Create a unique key for this model + parameter combination to prevent infinite loops
19666
+ const retryKey = `${modelName}-${unsupportedParameter}`;
19667
+ if (retriedUnsupportedParameters.has(retryKey)) {
19668
+ // Already retried this parameter, throw the error with attemptStack
19669
+ attemptStack.push({
19670
+ modelName,
19671
+ unsupportedParameter,
19672
+ errorMessage: error.message,
19673
+ stripped: true,
19674
+ });
19575
19675
  throw new PipelineExecutionError(`All attempts failed. Attempt history:\n` +
19576
19676
  attemptStack
19577
19677
  .map((a, i) => ` ${i + 1}. Model: ${a.modelName}` +
@@ -19581,52 +19681,25 @@ class OpenAiCompatibleExecutionTools {
19581
19681
  .join('\n') +
19582
19682
  `\nFinal error: ${error.message}`);
19583
19683
  }
19584
- throw error;
19585
- }
19586
- // Parse which parameter is unsupported
19587
- const unsupportedParameter = parseUnsupportedParameterError(error.message);
19588
- if (!unsupportedParameter) {
19684
+ // Mark this parameter as retried
19685
+ retriedUnsupportedParameters.add(retryKey);
19686
+ // Log warning in verbose mode
19589
19687
  if (this.options.isVerbose) {
19590
- console.warn(colors.bgYellow('Warning'), 'Could not parse unsupported parameter from error:', error.message);
19688
+ console.warn(colors.bgYellow('Warning'), `Removing unsupported parameter '${unsupportedParameter}' for model '${modelName}' and retrying request`);
19591
19689
  }
19592
- throw error;
19593
- }
19594
- // Create a unique key for this model + parameter combination to prevent infinite loops
19595
- const retryKey = `${modelName}-${unsupportedParameter}`;
19596
- if (retriedUnsupportedParameters.has(retryKey)) {
19597
- // Already retried this parameter, throw the error with attemptStack
19690
+ // Add to attemptStack
19598
19691
  attemptStack.push({
19599
19692
  modelName,
19600
19693
  unsupportedParameter,
19601
19694
  errorMessage: error.message,
19602
19695
  stripped: true,
19603
19696
  });
19604
- throw new PipelineExecutionError(`All attempts failed. Attempt history:\n` +
19605
- attemptStack
19606
- .map((a, i) => ` ${i + 1}. Model: ${a.modelName}` +
19607
- (a.unsupportedParameter ? `, Stripped: ${a.unsupportedParameter}` : '') +
19608
- `, Error: ${a.errorMessage}` +
19609
- (a.stripped ? ' (stripped and retried)' : ''))
19610
- .join('\n') +
19611
- `\nFinal error: ${error.message}`);
19612
- }
19613
- // Mark this parameter as retried
19614
- retriedUnsupportedParameters.add(retryKey);
19615
- // Log warning in verbose mode
19616
- if (this.options.isVerbose) {
19617
- console.warn(colors.bgYellow('Warning'), `Removing unsupported parameter '${unsupportedParameter}' for model '${modelName}' and retrying request`);
19697
+ // Remove the unsupported parameter and retry
19698
+ const modifiedModelRequirements = removeUnsupportedModelRequirement(currentModelRequirements, unsupportedParameter);
19699
+ return this.callChatModelWithRetry(prompt, modifiedModelRequirements, attemptStack, retriedUnsupportedParameters);
19618
19700
  }
19619
- // Add to attemptStack
19620
- attemptStack.push({
19621
- modelName,
19622
- unsupportedParameter,
19623
- errorMessage: error.message,
19624
- stripped: true,
19625
- });
19626
- // Remove the unsupported parameter and retry
19627
- const modifiedModelRequirements = removeUnsupportedModelRequirement(currentModelRequirements, unsupportedParameter);
19628
- return this.callChatModelWithRetry(prompt, modifiedModelRequirements, attemptStack, retriedUnsupportedParameters);
19629
19701
  }
19702
+ throw new PipelineExecutionError(`Tool calling loop did not return a result from ${this.title}`);
19630
19703
  }
19631
19704
  /**
19632
19705
  * Calls OpenAI API to use a complete model.
@@ -23419,6 +23492,79 @@ class FromCommitmentDefinition extends BaseCommitmentDefinition {
23419
23492
  * Note: [💞] Ignore a discrepancy between file name and entity name
23420
23493
  */
23421
23494
 
23495
+ /**
23496
+ * IMPORT commitment definition
23497
+ *
23498
+ * The IMPORT commitment tells the agent to import content from another agent at the current location.
23499
+ *
23500
+ * Example usage in agent source:
23501
+ *
23502
+ * ```book
23503
+ * IMPORT https://s6.ptbk.io/benjamin-white
23504
+ * ```
23505
+ *
23506
+ * @private [🪔] Maybe export the commitments through some package
23507
+ */
23508
+ class ImportCommitmentDefinition extends BaseCommitmentDefinition {
23509
+ constructor(type = 'IMPORT') {
23510
+ super(type);
23511
+ }
23512
+ /**
23513
+ * Short one-line description of IMPORT.
23514
+ */
23515
+ get description() {
23516
+ return 'Import content from another agent.';
23517
+ }
23518
+ /**
23519
+ * Icon for this commitment.
23520
+ */
23521
+ get icon() {
23522
+ return '📥';
23523
+ }
23524
+ /**
23525
+ * Markdown documentation for IMPORT commitment.
23526
+ */
23527
+ get documentation() {
23528
+ return spaceTrim$1(`
23529
+ # ${this.type}
23530
+
23531
+ Imports content from another agent at the location of the commitment.
23532
+
23533
+ ## Examples
23534
+
23535
+ \`\`\`book
23536
+ My AI Agent
23537
+
23538
+ IMPORT https://s6.ptbk.io/benjamin-white
23539
+ RULE Speak only in English.
23540
+ \`\`\`
23541
+ `);
23542
+ }
23543
+ applyToAgentModelRequirements(requirements, content) {
23544
+ const trimmedContent = content.trim();
23545
+ if (!trimmedContent) {
23546
+ return requirements;
23547
+ }
23548
+ if (!isValidAgentUrl(trimmedContent)) {
23549
+ throw new Error(spaceTrim$1((block) => `
23550
+ Invalid agent URL in IMPORT commitment: "${trimmedContent}"
23551
+
23552
+ \`\`\`book
23553
+ ${block(content)}
23554
+ \`\`\`
23555
+ `));
23556
+ }
23557
+ const importedAgentUrl = trimmedContent;
23558
+ return {
23559
+ ...requirements,
23560
+ importedAgentUrls: [...(requirements.importedAgentUrls || []), importedAgentUrl],
23561
+ };
23562
+ }
23563
+ }
23564
+ /**
23565
+ * Note: [💞] Ignore a discrepancy between file name and entity name
23566
+ */
23567
+
23422
23568
  /**
23423
23569
  * GOAL commitment definition
23424
23570
  *
@@ -25780,7 +25926,7 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
25780
25926
  // Add 'web_browser' to tools if not already present
25781
25927
  const updatedTools = existingTools.some((tool) => tool.name === 'web_browser')
25782
25928
  ? existingTools
25783
- : [
25929
+ : ([
25784
25930
  // TODO: [🔰] Use through proper MCP server
25785
25931
  ...existingTools,
25786
25932
  {
@@ -25800,7 +25946,7 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
25800
25946
  required: ['url'],
25801
25947
  },
25802
25948
  },
25803
- ];
25949
+ ]);
25804
25950
  // Return requirements with updated tools and metadata
25805
25951
  return {
25806
25952
  ...requirements,
@@ -26102,6 +26248,8 @@ const COMMITMENT_REGISTRY = [
26102
26248
  new FormatCommitmentDefinition('FORMAT'),
26103
26249
  new FormatCommitmentDefinition('FORMATS'),
26104
26250
  new FromCommitmentDefinition('FROM'),
26251
+ new ImportCommitmentDefinition('IMPORT'),
26252
+ new ImportCommitmentDefinition('IMPORTS'),
26105
26253
  new ModelCommitmentDefinition('MODEL'),
26106
26254
  new ModelCommitmentDefinition('MODELS'),
26107
26255
  new ActionCommitmentDefinition('ACTION'),