@promptbook/core 0.105.0-1 → 0.105.0-4

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 (40) hide show
  1. package/README.md +36 -77
  2. package/esm/index.es.js +923 -325
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/servers.d.ts +6 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  6. package/esm/typings/src/_packages/types.index.d.ts +4 -0
  7. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +10 -3
  8. package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +11 -1
  9. package/esm/typings/src/book-2.0/agent-source/communication-samples.test.d.ts +1 -0
  10. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.blocks.test.d.ts +1 -0
  11. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.import.test.d.ts +1 -0
  12. package/esm/typings/src/book-2.0/agent-source/parseAgentSource.import.test.d.ts +1 -0
  13. package/esm/typings/src/book-2.0/agent-source/parseAgentSourceWithCommitments.blocks.test.d.ts +1 -0
  14. package/esm/typings/src/commitments/USE_TIME/USE_TIME.d.ts +40 -0
  15. package/esm/typings/src/commitments/USE_TIME/USE_TIME.test.d.ts +1 -0
  16. package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +8 -0
  17. package/esm/typings/src/commitments/_base/CommitmentDefinition.d.ts +8 -0
  18. package/esm/typings/src/commitments/index.d.ts +11 -2
  19. package/esm/typings/src/config.d.ts +1 -0
  20. package/esm/typings/src/import-plugins/$fileImportPlugins.d.ts +7 -0
  21. package/esm/typings/src/import-plugins/AgentFileImportPlugin.d.ts +7 -0
  22. package/esm/typings/src/import-plugins/FileImportPlugin.d.ts +24 -0
  23. package/esm/typings/src/import-plugins/JsonFileImportPlugin.d.ts +7 -0
  24. package/esm/typings/src/import-plugins/TextFileImportPlugin.d.ts +7 -0
  25. package/esm/typings/src/llm-providers/_common/utils/cache/cacheLlmTools.d.ts +2 -1
  26. package/esm/typings/src/llm-providers/_common/utils/count-total-usage/countUsage.d.ts +2 -2
  27. package/esm/typings/src/llm-providers/agent/Agent.d.ts +9 -2
  28. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +3 -1
  29. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +10 -0
  30. package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -1
  31. package/esm/typings/src/scripting/javascript/JavascriptExecutionToolsOptions.d.ts +6 -1
  32. package/esm/typings/src/types/ModelRequirements.d.ts +6 -12
  33. package/esm/typings/src/utils/execCommand/$execCommandNormalizeOptions.d.ts +2 -3
  34. package/esm/typings/src/utils/execCommand/ExecCommandOptions.d.ts +7 -1
  35. package/esm/typings/src/utils/organization/keepImported.d.ts +9 -0
  36. package/esm/typings/src/utils/organization/keepTypeImported.d.ts +0 -1
  37. package/esm/typings/src/version.d.ts +1 -1
  38. package/package.json +1 -1
  39. package/umd/index.umd.js +923 -324
  40. package/umd/index.umd.js.map +1 -1
package/esm/index.es.js CHANGED
@@ -27,7 +27,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
27
27
  * @generated
28
28
  * @see https://github.com/webgptorg/promptbook
29
29
  */
30
- const PROMPTBOOK_ENGINE_VERSION = '0.105.0-1';
30
+ const PROMPTBOOK_ENGINE_VERSION = '0.105.0-4';
31
31
  /**
32
32
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
33
33
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -992,6 +992,7 @@ const PROMPTBOOK_SYNTAX_COLORS = {
992
992
  SEPARATOR: Color.fromHex('#cccccc'),
993
993
  COMMITMENT: Color.fromHex('#DA0F78'),
994
994
  PARAMETER: Color.fromHex('#8e44ad'),
995
+ CODE_BLOCK: Color.fromHex('#7700ffff'),
995
996
  };
996
997
  // <- TODO: [🧠][🈵] Using `Color` here increases the package size approx 3kb, maybe remove it
997
998
  /**
@@ -3649,74 +3650,90 @@ function addUsage(...usageItems) {
3649
3650
  * in real-time through an observable.
3650
3651
  *
3651
3652
  * @param llmTools - The LLM tools to be intercepted and tracked
3652
- * @returns An augmented version of the tools that includes usage tracking capabilities
3653
+ * @returns Full proxy of the tools with added usage tracking capabilities
3653
3654
  * @public exported from `@promptbook/core`
3654
3655
  */
3655
3656
  function countUsage(llmTools) {
3656
3657
  let totalUsage = ZERO_USAGE;
3657
3658
  const spending = new Subject();
3658
- const proxyTools = {
3659
- get title() {
3660
- return `${llmTools.title} (+usage)`;
3661
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
3662
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
3663
- },
3664
- get description() {
3665
- return `${llmTools.description} (+usage)`;
3666
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
3667
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
3668
- },
3669
- checkConfiguration() {
3670
- return /* not await */ llmTools.checkConfiguration();
3671
- },
3672
- listModels() {
3673
- return /* not await */ llmTools.listModels();
3674
- },
3675
- spending() {
3676
- return spending.asObservable();
3677
- },
3678
- getTotalUsage() {
3679
- // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
3680
- return totalUsage;
3659
+ // Create a Proxy to intercept all property access and ensure full proxying of all properties
3660
+ const proxyTools = new Proxy(llmTools, {
3661
+ get(target, prop, receiver) {
3662
+ // Handle title property
3663
+ if (prop === 'title') {
3664
+ return `${target.title} (+usage)`;
3665
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
3666
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
3667
+ }
3668
+ // Handle description property
3669
+ if (prop === 'description') {
3670
+ return `${target.description} (+usage)`;
3671
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
3672
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
3673
+ }
3674
+ // Handle spending method (new method added by this wrapper)
3675
+ if (prop === 'spending') {
3676
+ return () => {
3677
+ return spending.asObservable();
3678
+ };
3679
+ }
3680
+ // Handle getTotalUsage method (new method added by this wrapper)
3681
+ if (prop === 'getTotalUsage') {
3682
+ // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
3683
+ return () => {
3684
+ return totalUsage;
3685
+ };
3686
+ }
3687
+ // Handle callChatModel method with usage counting
3688
+ if (prop === 'callChatModel' && target.callChatModel !== undefined) {
3689
+ return async (prompt) => {
3690
+ // console.info('[🚕] callChatModel through countTotalUsage');
3691
+ const promptResult = await target.callChatModel(prompt);
3692
+ totalUsage = addUsage(totalUsage, promptResult.usage);
3693
+ spending.next(promptResult.usage);
3694
+ return promptResult;
3695
+ };
3696
+ }
3697
+ // Handle callCompletionModel method with usage counting
3698
+ if (prop === 'callCompletionModel' && target.callCompletionModel !== undefined) {
3699
+ return async (prompt) => {
3700
+ // console.info('[🚕] callCompletionModel through countTotalUsage');
3701
+ const promptResult = await target.callCompletionModel(prompt);
3702
+ totalUsage = addUsage(totalUsage, promptResult.usage);
3703
+ spending.next(promptResult.usage);
3704
+ return promptResult;
3705
+ };
3706
+ }
3707
+ // Handle callEmbeddingModel method with usage counting
3708
+ if (prop === 'callEmbeddingModel' && target.callEmbeddingModel !== undefined) {
3709
+ return async (prompt) => {
3710
+ // console.info('[🚕] callEmbeddingModel through countTotalUsage');
3711
+ const promptResult = await target.callEmbeddingModel(prompt);
3712
+ totalUsage = addUsage(totalUsage, promptResult.usage);
3713
+ spending.next(promptResult.usage);
3714
+ return promptResult;
3715
+ };
3716
+ }
3717
+ // Handle callImageGenerationModel method with usage counting
3718
+ if (prop === 'callImageGenerationModel' && target.callImageGenerationModel !== undefined) {
3719
+ return async (prompt) => {
3720
+ // console.info('[🚕] callImageGenerationModel through countTotalUsage');
3721
+ const promptResult = await target.callImageGenerationModel(prompt);
3722
+ totalUsage = addUsage(totalUsage, promptResult.usage);
3723
+ spending.next(promptResult.usage);
3724
+ return promptResult;
3725
+ };
3726
+ }
3727
+ // <- Note: [🤖]
3728
+ // For all other properties and methods, delegate to the original target
3729
+ const value = Reflect.get(target, prop, receiver);
3730
+ // If it's a function, bind it to the target to preserve context
3731
+ if (typeof value === 'function') {
3732
+ return value.bind(target);
3733
+ }
3734
+ return value;
3681
3735
  },
3682
- };
3683
- if (llmTools.callChatModel !== undefined) {
3684
- proxyTools.callChatModel = async (prompt) => {
3685
- // console.info('[🚕] callChatModel through countTotalUsage');
3686
- const promptResult = await llmTools.callChatModel(prompt);
3687
- totalUsage = addUsage(totalUsage, promptResult.usage);
3688
- spending.next(promptResult.usage);
3689
- return promptResult;
3690
- };
3691
- }
3692
- if (llmTools.callCompletionModel !== undefined) {
3693
- proxyTools.callCompletionModel = async (prompt) => {
3694
- // console.info('[🚕] callCompletionModel through countTotalUsage');
3695
- const promptResult = await llmTools.callCompletionModel(prompt);
3696
- totalUsage = addUsage(totalUsage, promptResult.usage);
3697
- spending.next(promptResult.usage);
3698
- return promptResult;
3699
- };
3700
- }
3701
- if (llmTools.callEmbeddingModel !== undefined) {
3702
- proxyTools.callEmbeddingModel = async (prompt) => {
3703
- // console.info('[🚕] callEmbeddingModel through countTotalUsage');
3704
- const promptResult = await llmTools.callEmbeddingModel(prompt);
3705
- totalUsage = addUsage(totalUsage, promptResult.usage);
3706
- spending.next(promptResult.usage);
3707
- return promptResult;
3708
- };
3709
- }
3710
- if (llmTools.callImageGenerationModel !== undefined) {
3711
- proxyTools.callImageGenerationModel = async (prompt) => {
3712
- // console.info('[🚕] callImageGenerationModel through countTotalUsage');
3713
- const promptResult = await llmTools.callImageGenerationModel(prompt);
3714
- totalUsage = addUsage(totalUsage, promptResult.usage);
3715
- spending.next(promptResult.usage);
3716
- return promptResult;
3717
- };
3718
- }
3719
- // <- Note: [🤖]
3736
+ });
3720
3737
  return proxyTools;
3721
3738
  }
3722
3739
  /**
@@ -7494,6 +7511,40 @@ async function preparePersona(personaDescription, tools, options) {
7494
7511
  * TODO: [🏢] Check validity of `temperature` in pipeline
7495
7512
  */
7496
7513
 
7514
+ /**
7515
+ * Creates an empty/basic agent model requirements object
7516
+ * This serves as the starting point for the reduce-like pattern
7517
+ * where each commitment applies its changes to build the final requirements
7518
+ *
7519
+ * @public exported from `@promptbook/core`
7520
+ */
7521
+ function createEmptyAgentModelRequirements() {
7522
+ return {
7523
+ systemMessage: '',
7524
+ // modelName: 'gpt-5',
7525
+ modelName: 'gemini-2.5-flash-lite',
7526
+ temperature: 0.7,
7527
+ topP: 0.9,
7528
+ topK: 50,
7529
+ };
7530
+ }
7531
+ /**
7532
+ * Creates a basic agent model requirements with just the agent name
7533
+ * This is used when we have an agent name but no commitments
7534
+ *
7535
+ * @public exported from `@promptbook/core`
7536
+ */
7537
+ function createBasicAgentModelRequirements(agentName) {
7538
+ const empty = createEmptyAgentModelRequirements();
7539
+ return {
7540
+ ...empty,
7541
+ systemMessage: `You are ${agentName || 'AI Agent'}`,
7542
+ };
7543
+ }
7544
+ /**
7545
+ * TODO: [🐤] Deduplicate `AgentModelRequirements` and `ModelRequirements` model requirements
7546
+ */
7547
+
7497
7548
  /**
7498
7549
  * Generates a regex pattern to match a specific commitment
7499
7550
  *
@@ -7610,6 +7661,14 @@ class BaseCommitmentDefinition {
7610
7661
  return this.appendToSystemMessage(requirements, commentSection);
7611
7662
  }
7612
7663
  }
7664
+ /**
7665
+ * Gets tool function implementations provided by this commitment
7666
+ *
7667
+ * When the `applyToAgentModelRequirements` adds tools to the requirements, this method should return the corresponding function definitions.
7668
+ */
7669
+ getToolFunctions() {
7670
+ return {};
7671
+ }
7613
7672
  }
7614
7673
 
7615
7674
  /**
@@ -8708,79 +8767,6 @@ class FromCommitmentDefinition extends BaseCommitmentDefinition {
8708
8767
  * Note: [💞] Ignore a discrepancy between file name and entity name
8709
8768
  */
8710
8769
 
8711
- /**
8712
- * IMPORT commitment definition
8713
- *
8714
- * The IMPORT commitment tells the agent to import content from another agent at the current location.
8715
- *
8716
- * Example usage in agent source:
8717
- *
8718
- * ```book
8719
- * IMPORT https://s6.ptbk.io/benjamin-white
8720
- * ```
8721
- *
8722
- * @private [🪔] Maybe export the commitments through some package
8723
- */
8724
- class ImportCommitmentDefinition extends BaseCommitmentDefinition {
8725
- constructor(type = 'IMPORT') {
8726
- super(type);
8727
- }
8728
- /**
8729
- * Short one-line description of IMPORT.
8730
- */
8731
- get description() {
8732
- return 'Import content from another agent.';
8733
- }
8734
- /**
8735
- * Icon for this commitment.
8736
- */
8737
- get icon() {
8738
- return '📥';
8739
- }
8740
- /**
8741
- * Markdown documentation for IMPORT commitment.
8742
- */
8743
- get documentation() {
8744
- return spaceTrim$1(`
8745
- # ${this.type}
8746
-
8747
- Imports content from another agent at the location of the commitment.
8748
-
8749
- ## Examples
8750
-
8751
- \`\`\`book
8752
- My AI Agent
8753
-
8754
- IMPORT https://s6.ptbk.io/benjamin-white
8755
- RULE Speak only in English.
8756
- \`\`\`
8757
- `);
8758
- }
8759
- applyToAgentModelRequirements(requirements, content) {
8760
- const trimmedContent = content.trim();
8761
- if (!trimmedContent) {
8762
- return requirements;
8763
- }
8764
- if (!isValidAgentUrl(trimmedContent)) {
8765
- throw new Error(spaceTrim$1((block) => `
8766
- Invalid agent URL in IMPORT commitment: "${trimmedContent}"
8767
-
8768
- \`\`\`book
8769
- ${block(content)}
8770
- \`\`\`
8771
- `));
8772
- }
8773
- const importedAgentUrl = trimmedContent;
8774
- return {
8775
- ...requirements,
8776
- importedAgentUrls: [...(requirements.importedAgentUrls || []), importedAgentUrl],
8777
- };
8778
- }
8779
- }
8780
- /**
8781
- * Note: [💞] Ignore a discrepancy between file name and entity name
8782
- */
8783
-
8784
8770
  /**
8785
8771
  * GOAL commitment definition
8786
8772
  *
@@ -8881,6 +8867,87 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
8881
8867
  * Note: [💞] Ignore a discrepancy between file name and entity name
8882
8868
  */
8883
8869
 
8870
+ /**
8871
+ * IMPORT commitment definition
8872
+ *
8873
+ * The IMPORT commitment tells the agent to import content from another agent at the current location.
8874
+ *
8875
+ * Example usage in agent source:
8876
+ *
8877
+ * ```book
8878
+ * IMPORT https://s6.ptbk.io/benjamin-white
8879
+ * ```
8880
+ *
8881
+ * @private [🪔] Maybe export the commitments through some package
8882
+ */
8883
+ class ImportCommitmentDefinition extends BaseCommitmentDefinition {
8884
+ constructor(type = 'IMPORT') {
8885
+ super(type);
8886
+ }
8887
+ /**
8888
+ * Short one-line description of IMPORT.
8889
+ */
8890
+ get description() {
8891
+ return 'Import content from another agent or a generic text file.';
8892
+ }
8893
+ /**
8894
+ * Icon for this commitment.
8895
+ */
8896
+ get icon() {
8897
+ return '📥';
8898
+ }
8899
+ /**
8900
+ * Markdown documentation for IMPORT commitment.
8901
+ */
8902
+ get documentation() {
8903
+ return spaceTrim$1(`
8904
+ # ${this.type}
8905
+
8906
+ Imports content from another agent or a generic text file at the location of the commitment.
8907
+
8908
+ ## Examples
8909
+
8910
+ \`\`\`book
8911
+ My AI Agent
8912
+
8913
+ IMPORT https://s6.ptbk.io/benjamin-white
8914
+ IMPORT https://example.com/some-text-file.txt
8915
+ IMPORT ./path/to/local-file.json
8916
+ RULE Speak only in English.
8917
+ \`\`\`
8918
+ `);
8919
+ }
8920
+ applyToAgentModelRequirements(requirements, content) {
8921
+ const trimmedContent = content.trim();
8922
+ if (!trimmedContent) {
8923
+ return requirements;
8924
+ }
8925
+ if (isValidAgentUrl(trimmedContent)) {
8926
+ const importedAgentUrl = trimmedContent;
8927
+ return {
8928
+ ...requirements,
8929
+ importedAgentUrls: [...(requirements.importedAgentUrls || []), importedAgentUrl],
8930
+ };
8931
+ }
8932
+ if (isValidUrl(trimmedContent) || isValidFilePath(trimmedContent)) {
8933
+ return {
8934
+ ...requirements,
8935
+ importedFileUrls: [...(requirements.importedFileUrls || []), trimmedContent],
8936
+ };
8937
+ }
8938
+ throw new Error(spaceTrim$1((block) => `
8939
+ Invalid agent URL or file path in IMPORT commitment: "${trimmedContent}"
8940
+
8941
+ \`\`\`book
8942
+ ${block(content)}
8943
+ \`\`\`
8944
+ `));
8945
+ }
8946
+ }
8947
+ /**
8948
+ * Note: [💞] Ignore a discrepancy between file name and entity name
8949
+ */
8950
+
8884
8951
  /**
8885
8952
  * KNOWLEDGE commitment definition
8886
8953
  *
@@ -9300,7 +9367,13 @@ class InitialMessageCommitmentDefinition extends BaseCommitmentDefinition {
9300
9367
  `);
9301
9368
  }
9302
9369
  applyToAgentModelRequirements(requirements, content) {
9303
- return requirements;
9370
+ // INITIAL MESSAGE is for UI display purposes and for conversation history construction.
9371
+ const newSample = { question: null, answer: content };
9372
+ const newSamples = [...(requirements.samples || []), newSample];
9373
+ return {
9374
+ ...requirements,
9375
+ samples: newSamples,
9376
+ };
9304
9377
  }
9305
9378
  }
9306
9379
 
@@ -10332,27 +10405,16 @@ class NoteCommitmentDefinition extends BaseCommitmentDefinition {
10332
10405
  `);
10333
10406
  }
10334
10407
  applyToAgentModelRequirements(requirements, content) {
10335
- var _a;
10336
10408
  // The NOTE commitment makes no changes to the system message or model requirements
10337
10409
  // It only stores the note content in metadata for documentation purposes
10338
- const trimmedContent = content.trim();
10339
- if (!trimmedContent) {
10410
+ const trimmedContent = spaceTrim$1(content);
10411
+ if (trimmedContent === '') {
10340
10412
  return requirements;
10341
10413
  }
10342
- // Get existing note content from metadata
10343
- const existingNoteContent = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.NOTE) || '';
10344
- // Merge the new content with existing note content
10345
- // When multiple NOTE commitments exist, they are aggregated together
10346
- const mergedNoteContent = existingNoteContent ? `${existingNoteContent}\n${trimmedContent}` : trimmedContent;
10347
- // Store the merged note content in metadata for debugging and inspection
10348
- const updatedMetadata = {
10349
- ...requirements.metadata,
10350
- NOTE: mergedNoteContent,
10351
- };
10352
- // Return requirements with updated metadata but no changes to system message
10414
+ // Return requirements with updated notes but no changes to system message
10353
10415
  return {
10354
10416
  ...requirements,
10355
- metadata: updatedMetadata,
10417
+ notes: [...(requirements.notes || []), trimmedContent],
10356
10418
  };
10357
10419
  }
10358
10420
  }
@@ -11371,6 +11433,136 @@ class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
11371
11433
  * Note: [💞] Ignore a discrepancy between file name and entity name
11372
11434
  */
11373
11435
 
11436
+ /**
11437
+ * USE TIME commitment definition
11438
+ *
11439
+ * The `USE TIME` commitment indicates that the agent should be able to determine the current date and time.
11440
+ *
11441
+ * Example usage in agent source:
11442
+ *
11443
+ * ```book
11444
+ * USE TIME
11445
+ * ```
11446
+ *
11447
+ * @private [🪔] Maybe export the commitments through some package
11448
+ */
11449
+ class UseTimeCommitmentDefinition extends BaseCommitmentDefinition {
11450
+ constructor() {
11451
+ super('USE TIME', ['CURRENT TIME', 'TIME', 'DATE']);
11452
+ }
11453
+ /**
11454
+ * Short one-line description of USE TIME.
11455
+ */
11456
+ get description() {
11457
+ return 'Enable the agent to determine the current date and time.';
11458
+ }
11459
+ /**
11460
+ * Icon for this commitment.
11461
+ */
11462
+ get icon() {
11463
+ return '🕒';
11464
+ }
11465
+ /**
11466
+ * Markdown documentation for USE TIME commitment.
11467
+ */
11468
+ get documentation() {
11469
+ return spaceTrim$1(`
11470
+ # USE TIME
11471
+
11472
+ Enables the agent to determine the current date and time.
11473
+
11474
+ ## Key aspects
11475
+
11476
+ - This tool won't receive any input.
11477
+ - It outputs the current date and time as an ISO 8601 string.
11478
+ - Allows the agent to answer questions about the current time or date.
11479
+
11480
+ ## Examples
11481
+
11482
+ \`\`\`book
11483
+ Time-aware Assistant
11484
+
11485
+ PERSONA You are a helpful assistant who knows the current time.
11486
+ USE TIME
11487
+ \`\`\`
11488
+ `);
11489
+ }
11490
+ applyToAgentModelRequirements(requirements, content) {
11491
+ // Get existing tools array or create new one
11492
+ const existingTools = requirements.tools || [];
11493
+ // Add 'get_current_time' to tools if not already present
11494
+ const updatedTools = existingTools.some((tool) => tool.name === 'get_current_time')
11495
+ ? existingTools
11496
+ : [
11497
+ ...existingTools,
11498
+ {
11499
+ name: 'get_current_time',
11500
+ description: 'Get the current date and time in ISO 8601 format.',
11501
+ parameters: {
11502
+ type: 'object',
11503
+ properties: {
11504
+ timezone: {
11505
+ type: 'string',
11506
+ description: 'Optional timezone name (e.g. "Europe/Prague", "UTC", "America/New_York").',
11507
+ },
11508
+ },
11509
+ required: [],
11510
+ },
11511
+ },
11512
+ // <- TODO: !!!! define the function in LLM tools
11513
+ ];
11514
+ // Return requirements with updated tools and metadata
11515
+ return {
11516
+ ...requirements,
11517
+ tools: updatedTools,
11518
+ metadata: {
11519
+ ...requirements.metadata,
11520
+ },
11521
+ };
11522
+ }
11523
+ /**
11524
+ * Gets the `get_current_time` tool function implementation.
11525
+ */
11526
+ getToolFunctions() {
11527
+ return {
11528
+ async get_current_time(args) {
11529
+ var _a;
11530
+ console.log('!!!! [Tool] get_current_time called', { args });
11531
+ const { timezone } = args;
11532
+ if (!timezone) {
11533
+ return new Date().toISOString();
11534
+ }
11535
+ try {
11536
+ // Note: Returning ISO 8601 string but in the requested timezone
11537
+ const formatter = new Intl.DateTimeFormat('en-CA', {
11538
+ timeZone: timezone,
11539
+ year: 'numeric',
11540
+ month: '2-digit',
11541
+ day: '2-digit',
11542
+ hour: '2-digit',
11543
+ minute: '2-digit',
11544
+ second: '2-digit',
11545
+ hour12: false,
11546
+ timeZoneName: 'shortOffset',
11547
+ });
11548
+ const parts = formatter.formatToParts(new Date());
11549
+ const part = (type) => { var _a; return (_a = parts.find((p) => p.type === type)) === null || _a === void 0 ? void 0 : _a.value; };
11550
+ // en-CA format is YYYY-MM-DD
11551
+ const isoString = `${part('year')}-${part('month')}-${part('day')}T${part('hour')}:${part('minute')}:${part('second')}${(_a = part('timeZoneName')) === null || _a === void 0 ? void 0 : _a.replace('GMT', '')}`;
11552
+ return isoString;
11553
+ }
11554
+ catch (error) {
11555
+ // Fallback to UTC if timezone is invalid
11556
+ return new Date().toISOString();
11557
+ }
11558
+ },
11559
+ };
11560
+ }
11561
+ }
11562
+ /**
11563
+ * Note: [💞] Ignore a discrepancy between file name and entity name
11564
+ */
11565
+
11374
11566
  /**
11375
11567
  * Placeholder commitment definition for commitments that are not yet implemented
11376
11568
  *
@@ -11438,7 +11630,6 @@ class NotYetImplementedCommitmentDefinition extends BaseCommitmentDefinition {
11438
11630
  }
11439
11631
  }
11440
11632
 
11441
- // Import all commitment definition classes
11442
11633
  /**
11443
11634
  * Registry of all available commitment definitions
11444
11635
  * This array contains instances of all commitment definitions
@@ -11455,10 +11646,10 @@ const COMMITMENT_REGISTRY = [
11455
11646
  new MemoryCommitmentDefinition('MEMORIES'),
11456
11647
  new StyleCommitmentDefinition('STYLE'),
11457
11648
  new StyleCommitmentDefinition('STYLES'),
11458
- new RuleCommitmentDefinition('RULE'),
11459
11649
  new RuleCommitmentDefinition('RULES'),
11460
- new LanguageCommitmentDefinition('LANGUAGE'),
11650
+ new RuleCommitmentDefinition('RULE'),
11461
11651
  new LanguageCommitmentDefinition('LANGUAGES'),
11652
+ new LanguageCommitmentDefinition('LANGUAGE'),
11462
11653
  new SampleCommitmentDefinition('SAMPLE'),
11463
11654
  new SampleCommitmentDefinition('EXAMPLE'),
11464
11655
  new FormatCommitmentDefinition('FORMAT'),
@@ -11498,6 +11689,7 @@ const COMMITMENT_REGISTRY = [
11498
11689
  new ClosedCommitmentDefinition(),
11499
11690
  new UseBrowserCommitmentDefinition(),
11500
11691
  new UseSearchEngineCommitmentDefinition(),
11692
+ new UseTimeCommitmentDefinition(),
11501
11693
  new UseMcpCommitmentDefinition(),
11502
11694
  new UseCommitmentDefinition(),
11503
11695
  // Not yet implemented commitments (using placeholder)
@@ -11577,51 +11769,32 @@ function getGroupedCommitmentDefinitions() {
11577
11769
  lastGroup.aliases.push(commitment.type);
11578
11770
  }
11579
11771
  else {
11580
- groupedCommitments.push({
11581
- primary: commitment,
11582
- aliases: [],
11583
- });
11584
- }
11585
- }
11586
- return $deepFreeze(groupedCommitments);
11587
- }
11588
- /**
11589
- * TODO: [🧠] Maybe create through standardized $register
11590
- * Note: [💞] Ignore a discrepancy between file name and entity name
11591
- */
11592
-
11593
- /**
11594
- * Creates an empty/basic agent model requirements object
11595
- * This serves as the starting point for the reduce-like pattern
11596
- * where each commitment applies its changes to build the final requirements
11597
- *
11598
- * @public exported from `@promptbook/core`
11599
- */
11600
- function createEmptyAgentModelRequirements() {
11601
- return {
11602
- systemMessage: '',
11603
- // modelName: 'gpt-5',
11604
- modelName: 'gemini-2.5-flash-lite',
11605
- temperature: 0.7,
11606
- topP: 0.9,
11607
- topK: 50,
11608
- };
11772
+ groupedCommitments.push({
11773
+ primary: commitment,
11774
+ aliases: [],
11775
+ });
11776
+ }
11777
+ }
11778
+ return $deepFreeze(groupedCommitments);
11609
11779
  }
11610
11780
  /**
11611
- * Creates a basic agent model requirements with just the agent name
11612
- * This is used when we have an agent name but no commitments
11781
+ * Gets all function implementations provided by all commitments
11613
11782
  *
11614
11783
  * @public exported from `@promptbook/core`
11615
11784
  */
11616
- function createBasicAgentModelRequirements(agentName) {
11617
- const empty = createEmptyAgentModelRequirements();
11618
- return {
11619
- ...empty,
11620
- systemMessage: `You are ${agentName || 'AI Agent'}`,
11621
- };
11785
+ function getAllCommitmentsToolFunctions() {
11786
+ const allToolFunctions = {};
11787
+ for (const commitmentDefinition of getAllCommitmentDefinitions()) {
11788
+ const toolFunctions = commitmentDefinition.getToolFunctions();
11789
+ for (const [funcName, funcImpl] of Object.entries(toolFunctions)) {
11790
+ allToolFunctions[funcName] = funcImpl;
11791
+ }
11792
+ }
11793
+ return allToolFunctions;
11622
11794
  }
11623
11795
  /**
11624
- * TODO: [🐤] Deduplicate `AgentModelRequirements` and `ModelRequirements` model requirements
11796
+ * TODO: [🧠] Maybe create through standardized $register
11797
+ * Note: [💞] Ignore a discrepancy between file name and entity name
11625
11798
  */
11626
11799
 
11627
11800
  /**
@@ -11702,11 +11875,37 @@ function parseAgentSourceWithCommitments(agentSource) {
11702
11875
  let currentCommitment = null;
11703
11876
  // Process lines starting from after the agent name line
11704
11877
  const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
11878
+ let isInsideCodeBlock = false;
11705
11879
  for (let i = startIndex; i < lines.length; i++) {
11706
11880
  const line = lines[i];
11707
11881
  if (line === undefined) {
11708
11882
  continue;
11709
11883
  }
11884
+ const trimmedLine = line.trim();
11885
+ // Check if this line starts or ends a code block
11886
+ if (trimmedLine.startsWith('```')) {
11887
+ isInsideCodeBlock = !isInsideCodeBlock;
11888
+ if (currentCommitment) {
11889
+ // If we are inside a commitment, the code block is part of it
11890
+ currentCommitment.contentLines.push(line);
11891
+ }
11892
+ else {
11893
+ // If we are not inside a commitment, the code block is non-commitment
11894
+ nonCommitmentLines.push(line);
11895
+ }
11896
+ continue;
11897
+ }
11898
+ if (isInsideCodeBlock) {
11899
+ if (currentCommitment) {
11900
+ // If we are inside a commitment and a code block, the line is part of the commitment
11901
+ currentCommitment.contentLines.push(line);
11902
+ }
11903
+ else {
11904
+ // If we are inside a code block but not a commitment, the line is non-commitment
11905
+ nonCommitmentLines.push(line);
11906
+ }
11907
+ continue;
11908
+ }
11710
11909
  // Check if this line starts a new commitment
11711
11910
  let foundNewCommitment = false;
11712
11911
  for (const definition of COMMITMENT_REGISTRY) {
@@ -11784,6 +11983,97 @@ function parseAgentSourceWithCommitments(agentSource) {
11784
11983
  };
11785
11984
  }
11786
11985
 
11986
+ /**
11987
+ * Plugin for importing agent books *(`.book` files)*
11988
+ *
11989
+ * @private [🥝] Maybe export the import plugins through some package
11990
+ */
11991
+ const AgentFileImportPlugin = {
11992
+ name: 'agent-file-import-plugin',
11993
+ canImport(mimeType) {
11994
+ // [🧠] Should we have a specific MIME type for agent books?
11995
+ // For now, let's assume it's identified by .book extension or certain MIME types if provided
11996
+ return mimeType === 'text/x-promptbook' || mimeType === 'application/x-promptbook';
11997
+ },
11998
+ import(content) {
11999
+ const parseResult = parseAgentSourceWithCommitments(content);
12000
+ // Bring only the agent corpus (non-commitment lines and relevant commitments)
12001
+ // Stripping the agent name (which is usually the first line)
12002
+ const corpus = parseResult.nonCommitmentLines
12003
+ .filter((line, index) => index > 0 || !parseResult.agentName)
12004
+ .join('\n')
12005
+ .trim();
12006
+ // Also include relevant commitments that make up the "corpus" of the agent
12007
+ // For example PERSONA, RULE, KNOWLEDGE
12008
+ const relevantCommitments = parseResult.commitments
12009
+ .filter((c) => ['PERSONA', 'RULE', 'KNOWLEDGE'].includes(c.type))
12010
+ .map((c) => `${c.type} ${c.content}`)
12011
+ .join('\n\n');
12012
+ return spaceTrim$1((block) => `
12013
+ ${block(relevantCommitments)}
12014
+
12015
+ ${block(corpus)}
12016
+ `).trim();
12017
+ },
12018
+ };
12019
+
12020
+ /**
12021
+ * Plugin for importing JSON files
12022
+ *
12023
+ * @private [🥝] Maybe export the import plugins through some package
12024
+ */
12025
+ const JsonFileImportPlugin = {
12026
+ name: 'json-file-import-plugin',
12027
+ canImport(mimeType) {
12028
+ return mimeType === 'application/json' || mimeType.endsWith('+json');
12029
+ },
12030
+ import(content) {
12031
+ try {
12032
+ const json = JSON.parse(content);
12033
+ const formattedJson = JSON.stringify(json, null, 4);
12034
+ return `\`\`\`json\n${formattedJson}\n\`\`\``;
12035
+ }
12036
+ catch (error) {
12037
+ // If JSON is invalid, still import it but maybe not as pretty JSON
12038
+ return `\`\`\`json\n${content}\n\`\`\``;
12039
+ }
12040
+ },
12041
+ };
12042
+
12043
+ /**
12044
+ * Plugin for importing generic text files
12045
+ *
12046
+ * @private [🥝] Maybe export the import plugins through some package
12047
+ */
12048
+ const TextFileImportPlugin = {
12049
+ name: 'text-file-import-plugin',
12050
+ canImport(mimeType) {
12051
+ return (mimeType === 'text/plain' ||
12052
+ mimeType === 'text/markdown' ||
12053
+ mimeType === 'text/x-typescript' ||
12054
+ mimeType === 'text/javascript' ||
12055
+ mimeType === 'text/css' ||
12056
+ mimeType === 'text/html' ||
12057
+ mimeType.startsWith('text/'));
12058
+ },
12059
+ import(content, mimeType) {
12060
+ const extension = mimeTypeToExtension(mimeType);
12061
+ const codeBlockType = extension || 'txt';
12062
+ return `\`\`\`${codeBlockType}\n${content}\n\`\`\``;
12063
+ },
12064
+ };
12065
+
12066
+ /**
12067
+ * All available file import plugins
12068
+ *
12069
+ * @private [🥝] Maybe export the import plugins through some package
12070
+ */
12071
+ const $fileImportPlugins = [
12072
+ AgentFileImportPlugin,
12073
+ JsonFileImportPlugin,
12074
+ TextFileImportPlugin,
12075
+ ];
12076
+
11787
12077
  /**
11788
12078
  * Parses parameters from text using both supported notations:
11789
12079
  * 1. @Parameter - single word parameter starting with @
@@ -11873,6 +12163,7 @@ function removeCommentsFromSystemMessage(systemMessage) {
11873
12163
  * @public exported from `@promptbook/core`
11874
12164
  */
11875
12165
  async function createAgentModelRequirementsWithCommitments(agentSource, modelName) {
12166
+ var _a;
11876
12167
  // Parse the agent source to extract commitments
11877
12168
  const parseResult = parseAgentSourceWithCommitments(agentSource);
11878
12169
  // Apply DELETE filtering: remove prior commitments tagged by parameters targeted by DELETE/CANCEL/DISCARD/REMOVE
@@ -11939,6 +12230,64 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
11939
12230
  }
11940
12231
  }
11941
12232
  }
12233
+ // Handle IMPORT commitments for generic files
12234
+ // Note: This logic could be moved to ImportCommitmentDefinition, but it needs to be asynchronous
12235
+ if (requirements.importedFileUrls && requirements.importedFileUrls.length > 0) {
12236
+ for (const fileUrl of requirements.importedFileUrls) {
12237
+ try {
12238
+ // 1. Mocked security check
12239
+ await mockedSecurityCheck(fileUrl);
12240
+ // 2. Fetch file content
12241
+ let content;
12242
+ let mimeType = null;
12243
+ if (isValidUrl(fileUrl)) {
12244
+ const response = await promptbookFetch(fileUrl);
12245
+ if (!response.ok) {
12246
+ throw new Error(`Failed to fetch ${fileUrl}: ${response.statusText}`);
12247
+ }
12248
+ content = await response.text();
12249
+ mimeType = response.headers.get('Content-Type');
12250
+ /*
12251
+ TODO: !!!! Commented out this case because we need to work in Browser-compatible mode in many packages, use passed `fs` instead
12252
+ } else if (isValidFilePath(fileUrl)) {
12253
+ // [x🟢x] This code is expected to run in Node environment if local files are used
12254
+ const fs = await import('fs/promises');
12255
+ content = await fs.readFile(fileUrl, 'utf-8');
12256
+ const extension = getFileExtension(fileUrl);
12257
+ mimeType = extensionToMimeType(extension as string);
12258
+ */
12259
+ }
12260
+ else {
12261
+ throw new Error(`Invalid file URL or path: ${fileUrl}`);
12262
+ }
12263
+ if (!mimeType) {
12264
+ mimeType = 'text/plain';
12265
+ }
12266
+ // Remove charset from mime type
12267
+ mimeType = mimeType.split(';')[0].trim();
12268
+ // 3. Prevent importing binary files (mocked check)
12269
+ if (isBinaryMimeType(mimeType)) {
12270
+ throw new Error(`Importing binary files is not allowed: ${mimeType}`);
12271
+ }
12272
+ // 4. Find appropriate plugin
12273
+ const plugin = $fileImportPlugins.find((p) => p.canImport(mimeType));
12274
+ if (!plugin) {
12275
+ throw new Error(`No import plugin found for MIME type: ${mimeType}`);
12276
+ }
12277
+ // 5. Process content
12278
+ const importedContent = await plugin.import(content, mimeType);
12279
+ // 6. Append to system message
12280
+ requirements = {
12281
+ ...requirements,
12282
+ systemMessage: requirements.systemMessage + '\n\n' + importedContent,
12283
+ };
12284
+ }
12285
+ catch (error) {
12286
+ console.warn(`Failed to import file ${fileUrl}:`, error);
12287
+ // Continue with other imports even if one fails
12288
+ }
12289
+ }
12290
+ }
11942
12291
  // Handle MCP servers (extract from original agent source)
11943
12292
  const mcpServers = extractMcpServers(agentSource);
11944
12293
  if (mcpServers.length > 0) {
@@ -11948,9 +12297,11 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
11948
12297
  };
11949
12298
  }
11950
12299
  // Add non-commitment lines to system message if they exist
12300
+ // Note: Filtering out horizontal lines (---) as requested
11951
12301
  const nonCommitmentContent = parseResult.nonCommitmentLines
11952
12302
  .filter((line, index) => index > 0 || !parseResult.agentName) // Skip first line if it's the agent name
11953
12303
  .filter((line) => line.trim()) // Remove empty lines
12304
+ .filter((line) => !/^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/.test(line)) // Remove horizontal lines
11954
12305
  .join('\n')
11955
12306
  .trim();
11956
12307
  if (nonCommitmentContent) {
@@ -11959,6 +12310,26 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
11959
12310
  systemMessage: requirements.systemMessage + '\n\n' + nonCommitmentContent,
11960
12311
  };
11961
12312
  }
12313
+ // Add example interactions to the system message
12314
+ const examples = [];
12315
+ // 1. Initial message as an example agent response
12316
+ const initialMessage = (_a = parseResult.commitments.find((c) => c.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
12317
+ if (initialMessage) {
12318
+ examples.push(`Agent: ${initialMessage}`);
12319
+ }
12320
+ // 2. User and Agent message pairs
12321
+ if (requirements.samples && requirements.samples.length > 0) {
12322
+ for (const sample of requirements.samples) {
12323
+ examples.push(`User: ${sample.question}\nAgent: ${sample.answer}`);
12324
+ }
12325
+ }
12326
+ if (examples.length > 0) {
12327
+ const exampleInteractionsContent = `Example interaction:\n\n${examples.join('\n\n')}`;
12328
+ requirements = {
12329
+ ...requirements,
12330
+ systemMessage: requirements.systemMessage + '\n\n' + exampleInteractionsContent,
12331
+ };
12332
+ }
11962
12333
  // Remove comment lines (lines starting with #) from the final system message
11963
12334
  // while preserving the original content with comments in metadata
11964
12335
  const cleanedSystemMessage = removeCommentsFromSystemMessage(requirements.systemMessage);
@@ -11967,6 +12338,36 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
11967
12338
  systemMessage: cleanedSystemMessage,
11968
12339
  };
11969
12340
  }
12341
+ /**
12342
+ * Mocked security check for imported files
12343
+ *
12344
+ * @param urlOrPath - The URL or local path of the file to check
12345
+ * @returns A promise that resolves if the file is safe
12346
+ */
12347
+ async function mockedSecurityCheck(urlOrPath) {
12348
+ // TODO: Implement proper security checks
12349
+ await new Promise((resolve) => setTimeout(resolve, 10)); // Mock async delay
12350
+ if (urlOrPath.includes('malicious')) {
12351
+ throw new Error(`Security check failed for: ${urlOrPath}`);
12352
+ }
12353
+ }
12354
+ /**
12355
+ * Checks if the given MIME type belongs to a binary file
12356
+ *
12357
+ * @param mimeType - The MIME type to check
12358
+ * @returns True if it's a binary MIME type
12359
+ */
12360
+ function isBinaryMimeType(mimeType) {
12361
+ const binaryPrefixes = [
12362
+ 'image/',
12363
+ 'video/',
12364
+ 'audio/',
12365
+ 'application/octet-stream',
12366
+ 'application/pdf',
12367
+ 'application/zip',
12368
+ ];
12369
+ return binaryPrefixes.some((prefix) => mimeType.startsWith(prefix));
12370
+ }
11970
12371
 
11971
12372
  /**
11972
12373
  * Normalizes agent name from arbitrary string to valid agent name
@@ -12027,7 +12428,24 @@ function parseAgentSource(agentSource) {
12027
12428
  const meta = {};
12028
12429
  const links = [];
12029
12430
  const capabilities = [];
12431
+ const samples = [];
12432
+ let pendingUserMessage = null;
12030
12433
  for (const commitment of parseResult.commitments) {
12434
+ if (commitment.type === 'INITIAL MESSAGE') {
12435
+ samples.push({ question: null, answer: commitment.content });
12436
+ continue;
12437
+ }
12438
+ if (commitment.type === 'USER MESSAGE') {
12439
+ pendingUserMessage = commitment.content;
12440
+ continue;
12441
+ }
12442
+ if (commitment.type === 'AGENT MESSAGE') {
12443
+ if (pendingUserMessage !== null) {
12444
+ samples.push({ question: pendingUserMessage, answer: commitment.content });
12445
+ pendingUserMessage = null;
12446
+ }
12447
+ continue;
12448
+ }
12031
12449
  if (commitment.type === 'USE BROWSER') {
12032
12450
  capabilities.push({
12033
12451
  type: 'browser',
@@ -12044,6 +12462,37 @@ function parseAgentSource(agentSource) {
12044
12462
  });
12045
12463
  continue;
12046
12464
  }
12465
+ if (commitment.type === 'USE TIME') {
12466
+ capabilities.push({
12467
+ type: 'time',
12468
+ label: 'Time',
12469
+ iconName: 'Clock',
12470
+ });
12471
+ continue;
12472
+ }
12473
+ if (commitment.type === 'IMPORT') {
12474
+ const content = spaceTrim$2(commitment.content).split('\n')[0] || '';
12475
+ let label = content;
12476
+ const iconName = 'Download';
12477
+ try {
12478
+ if (content.startsWith('http://') || content.startsWith('https://')) {
12479
+ const url = new URL(content);
12480
+ label = url.hostname.replace(/^www\./, '') + '.../' + url.pathname.split('/').pop();
12481
+ }
12482
+ else if (content.startsWith('./') || content.startsWith('../') || content.startsWith('/')) {
12483
+ label = content.split('/').pop() || content;
12484
+ }
12485
+ }
12486
+ catch (e) {
12487
+ // Invalid URL or path, keep default label
12488
+ }
12489
+ capabilities.push({
12490
+ type: 'knowledge',
12491
+ label,
12492
+ iconName,
12493
+ });
12494
+ continue;
12495
+ }
12047
12496
  if (commitment.type === 'KNOWLEDGE') {
12048
12497
  const content = spaceTrim$2(commitment.content).split('\n')[0] || '';
12049
12498
  let label = content;
@@ -12128,6 +12577,7 @@ function parseAgentSource(agentSource) {
12128
12577
  links,
12129
12578
  parameters,
12130
12579
  capabilities,
12580
+ samples,
12131
12581
  };
12132
12582
  }
12133
12583
  /**
@@ -16758,6 +17208,9 @@ function filterModels(llmTools, predicate) {
16758
17208
  return originalModels.filter(predicate);
16759
17209
  }
16760
17210
  },
17211
+ checkConfiguration() {
17212
+ return /* not await */ llmTools.checkConfiguration();
17213
+ },
16761
17214
  };
16762
17215
  // Helper function to validate if a model is allowed
16763
17216
  async function isModelAllowed(modelName) {
@@ -17064,31 +17517,14 @@ class MemoryStorage {
17064
17517
  * Intercepts LLM tools and counts total usage of the tools
17065
17518
  *
17066
17519
  * Note: It can take extended `LlmExecutionTools` and cache the
17520
+ * Note: Returns full proxy of all LLM tool properties and methods
17067
17521
  *
17068
17522
  * @param llmTools LLM tools to be intercepted with usage counting, it can contain extra methods like `totalUsage`
17069
- * @returns LLM tools with same functionality with added total cost counting
17523
+ * @returns Full proxy of LLM tools with same functionality with added caching
17070
17524
  * @public exported from `@promptbook/core`
17071
17525
  */
17072
17526
  function cacheLlmTools(llmTools, options = {}) {
17073
17527
  const { storage = new MemoryStorage(), isCacheReloaded = false, isVerbose = DEFAULT_IS_VERBOSE } = options;
17074
- const proxyTools = {
17075
- ...llmTools,
17076
- // <- Note: [🥫]
17077
- get title() {
17078
- return `${llmTools.title} (cached)`;
17079
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
17080
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(cached)"?
17081
- },
17082
- get description() {
17083
- return `${llmTools.description} (cached)`;
17084
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
17085
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(cached)"?
17086
- },
17087
- listModels() {
17088
- // TODO: [🧠] Should be model listing also cached?
17089
- return /* not await */ llmTools.listModels();
17090
- },
17091
- };
17092
17528
  const callCommonModel = async (prompt) => {
17093
17529
  var _a;
17094
17530
  const { parameters, content, modelRequirements } = prompt;
@@ -17211,27 +17647,55 @@ function cacheLlmTools(llmTools, options = {}) {
17211
17647
  }
17212
17648
  return promptResult;
17213
17649
  };
17214
- if (llmTools.callChatModel !== undefined) {
17215
- proxyTools.callChatModel = async (prompt) => {
17216
- return /* not await */ callCommonModel(prompt);
17217
- };
17218
- }
17219
- if (llmTools.callCompletionModel !== undefined) {
17220
- proxyTools.callCompletionModel = async (prompt) => {
17221
- return /* not await */ callCommonModel(prompt);
17222
- };
17223
- }
17224
- if (llmTools.callEmbeddingModel !== undefined) {
17225
- proxyTools.callEmbeddingModel = async (prompt) => {
17226
- return /* not await */ callCommonModel(prompt);
17227
- };
17228
- }
17229
- if (llmTools.callImageGenerationModel !== undefined) {
17230
- proxyTools.callImageGenerationModel = async (prompt) => {
17231
- return /* not await */ callCommonModel(prompt);
17232
- };
17233
- }
17234
- // <- Note: [🤖]
17650
+ // Create a Proxy to intercept all property access and ensure full proxying of all properties
17651
+ const proxyTools = new Proxy(llmTools, {
17652
+ get(target, prop, receiver) {
17653
+ // Handle title property
17654
+ if (prop === 'title') {
17655
+ return `${target.title} (cached)`;
17656
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
17657
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(cached)"?
17658
+ }
17659
+ // Handle description property
17660
+ if (prop === 'description') {
17661
+ return `${target.description} (cached)`;
17662
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
17663
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(cached)"?
17664
+ }
17665
+ // Handle callChatModel method
17666
+ if (prop === 'callChatModel' && target.callChatModel !== undefined) {
17667
+ return async (prompt) => {
17668
+ return /* not await */ callCommonModel(prompt);
17669
+ };
17670
+ }
17671
+ // Handle callCompletionModel method
17672
+ if (prop === 'callCompletionModel' && target.callCompletionModel !== undefined) {
17673
+ return async (prompt) => {
17674
+ return /* not await */ callCommonModel(prompt);
17675
+ };
17676
+ }
17677
+ // Handle callEmbeddingModel method
17678
+ if (prop === 'callEmbeddingModel' && target.callEmbeddingModel !== undefined) {
17679
+ return async (prompt) => {
17680
+ return /* not await */ callCommonModel(prompt);
17681
+ };
17682
+ }
17683
+ // Handle callImageGenerationModel method
17684
+ if (prop === 'callImageGenerationModel' && target.callImageGenerationModel !== undefined) {
17685
+ return async (prompt) => {
17686
+ return /* not await */ callCommonModel(prompt);
17687
+ };
17688
+ }
17689
+ // <- Note: [🤖]
17690
+ // For all other properties and methods, delegate to the original target
17691
+ const value = Reflect.get(target, prop, receiver);
17692
+ // If it's a function, bind it to the target to preserve context
17693
+ if (typeof value === 'function') {
17694
+ return value.bind(target);
17695
+ }
17696
+ return value;
17697
+ },
17698
+ });
17235
17699
  return proxyTools;
17236
17700
  }
17237
17701
  /**
@@ -18478,7 +18942,7 @@ class OpenAiCompatibleExecutionTools {
18478
18942
  const args = ${functionArgs};
18479
18943
  return await ${functionName}(args);
18480
18944
  `,
18481
- parameters: {}, // <- TODO: [🧠] What parameters to pass?
18945
+ parameters: prompt.parameters,
18482
18946
  });
18483
18947
  }
18484
18948
  catch (error) {
@@ -19217,7 +19681,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19217
19681
  * Calls OpenAI API to use a chat model with streaming.
19218
19682
  */
19219
19683
  async callChatModelStream(prompt, onProgress) {
19220
- var _a, _b, _c;
19684
+ var _a, _b, _c, _d;
19221
19685
  if (this.options.isVerbose) {
19222
19686
  console.info('💬 OpenAI callChatModel call', { prompt });
19223
19687
  }
@@ -19272,6 +19736,131 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19272
19736
  }
19273
19737
  // Always add the current user message
19274
19738
  threadMessages.push({ role: 'user', content: rawPromptContent });
19739
+ // Check if tools are being used - if so, use non-streaming mode
19740
+ const hasTools = modelRequirements.tools !== undefined && modelRequirements.tools.length > 0;
19741
+ const start = $getCurrentDate();
19742
+ let complete;
19743
+ // [🐱‍🚀] When tools are present, we need to use the non-streaming Runs API
19744
+ // because streaming doesn't support tool execution flow properly
19745
+ if (hasTools) {
19746
+ const rawRequest = {
19747
+ assistant_id: this.assistantId,
19748
+ thread: {
19749
+ messages: threadMessages,
19750
+ },
19751
+ tools: mapToolsToOpenAi(modelRequirements.tools),
19752
+ };
19753
+ if (this.options.isVerbose) {
19754
+ console.info(colors.bgWhite('rawRequest (non-streaming with tools)'), JSON.stringify(rawRequest, null, 4));
19755
+ }
19756
+ // Create thread and run
19757
+ const threadAndRun = await client.beta.threads.createAndRun(rawRequest);
19758
+ let run = threadAndRun;
19759
+ // Poll until run completes or requires action
19760
+ while (run.status === 'queued' || run.status === 'in_progress' || run.status === 'requires_action') {
19761
+ if (run.status === 'requires_action' && ((_a = run.required_action) === null || _a === void 0 ? void 0 : _a.type) === 'submit_tool_outputs') {
19762
+ // Execute tools
19763
+ const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
19764
+ const toolOutputs = [];
19765
+ for (const toolCall of toolCalls) {
19766
+ if (toolCall.type === 'function') {
19767
+ const functionName = toolCall.function.name;
19768
+ const functionArgs = JSON.parse(toolCall.function.arguments);
19769
+ if (this.options.isVerbose) {
19770
+ console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
19771
+ }
19772
+ // Get execution tools for script execution
19773
+ const executionTools = this.options
19774
+ .executionTools;
19775
+ if (!executionTools || !executionTools.script) {
19776
+ throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
19777
+ }
19778
+ // TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
19779
+ const scriptTools = Array.isArray(executionTools.script)
19780
+ ? executionTools.script
19781
+ : [executionTools.script];
19782
+ let functionResponse;
19783
+ try {
19784
+ const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
19785
+ functionResponse = await scriptTool.execute({
19786
+ scriptLanguage: 'javascript',
19787
+ script: `
19788
+ const args = ${JSON.stringify(functionArgs)};
19789
+ return await ${functionName}(args);
19790
+ `,
19791
+ parameters: prompt.parameters,
19792
+ });
19793
+ if (this.options.isVerbose) {
19794
+ console.info(`✅ Tool ${functionName} executed:`, functionResponse);
19795
+ }
19796
+ }
19797
+ catch (error) {
19798
+ assertsError(error);
19799
+ functionResponse = spaceTrim$2((block) => `
19800
+
19801
+ The invoked tool \`${functionName}\` failed with error:
19802
+
19803
+ \`\`\`json
19804
+ ${block(JSON.stringify(serializeError(error), null, 4))}
19805
+ \`\`\`
19806
+
19807
+ `);
19808
+ console.error(colors.bgRed(`❌ Error executing tool ${functionName}:`));
19809
+ console.error(error);
19810
+ }
19811
+ toolOutputs.push({
19812
+ tool_call_id: toolCall.id,
19813
+ output: functionResponse,
19814
+ });
19815
+ }
19816
+ }
19817
+ // Submit tool outputs
19818
+ run = await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
19819
+ tool_outputs: toolOutputs,
19820
+ });
19821
+ }
19822
+ else {
19823
+ // Wait a bit before polling again
19824
+ await new Promise((resolve) => setTimeout(resolve, 500));
19825
+ run = await client.beta.threads.runs.retrieve(run.thread_id, run.id);
19826
+ }
19827
+ }
19828
+ if (run.status !== 'completed') {
19829
+ throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
19830
+ }
19831
+ // Get messages from the thread
19832
+ const messages = await client.beta.threads.messages.list(run.thread_id);
19833
+ const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
19834
+ if (assistantMessages.length === 0) {
19835
+ throw new PipelineExecutionError('No assistant messages found after run completion');
19836
+ }
19837
+ const lastMessage = assistantMessages[0];
19838
+ const textContent = lastMessage.content.find((c) => c.type === 'text');
19839
+ if (!textContent || textContent.type !== 'text') {
19840
+ throw new PipelineExecutionError('No text content in assistant response');
19841
+ }
19842
+ complete = $getCurrentDate();
19843
+ const resultContent = textContent.text.value;
19844
+ const usage = UNCERTAIN_USAGE;
19845
+ // Progress callback with final result
19846
+ const finalChunk = {
19847
+ content: resultContent,
19848
+ modelName: 'assistant',
19849
+ timing: { start, complete },
19850
+ usage,
19851
+ rawPromptContent,
19852
+ rawRequest,
19853
+ rawResponse: { run, messages: messages.data },
19854
+ };
19855
+ onProgress(finalChunk);
19856
+ return exportJson({
19857
+ name: 'promptResult',
19858
+ message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
19859
+ order: [],
19860
+ value: finalChunk,
19861
+ });
19862
+ }
19863
+ // Streaming mode (without tools)
19275
19864
  const rawRequest = {
19276
19865
  // TODO: [👨‍👨‍👧‍👧] ...modelSettings,
19277
19866
  // TODO: [👨‍👨‍👧‍👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
@@ -19282,10 +19871,8 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19282
19871
  tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
19283
19872
  // <- TODO: Add user identification here> user: this.options.user,
19284
19873
  };
19285
- const start = $getCurrentDate();
19286
- let complete;
19287
19874
  if (this.options.isVerbose) {
19288
- console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
19875
+ console.info(colors.bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
19289
19876
  }
19290
19877
  const stream = await client.beta.threads.createAndRunStream(rawRequest);
19291
19878
  stream.on('connect', () => {
@@ -19321,6 +19908,15 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19321
19908
  console.info('messageDone', message);
19322
19909
  }
19323
19910
  });
19911
+ // TODO: [🐱‍🚀] Handle tool calls in assistants
19912
+ // Note: OpenAI Assistant streaming with tool calls requires special handling.
19913
+ // The stream will pause when a tool call is needed, and we need to:
19914
+ // 1. Wait for the run to reach 'requires_action' status
19915
+ // 2. Execute the tool calls
19916
+ // 3. Submit tool outputs via a separate API call (not on the stream)
19917
+ // 4. Continue the run
19918
+ // This requires switching to non-streaming mode or using the Runs API directly.
19919
+ // For now, tools with assistants should use the non-streaming chat completions API instead.
19324
19920
  const rawResponse = await stream.finalMessages();
19325
19921
  if (this.options.isVerbose) {
19326
19922
  console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
@@ -19331,10 +19927,10 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19331
19927
  if (rawResponse[0].content.length !== 1) {
19332
19928
  throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
19333
19929
  }
19334
- if (((_a = rawResponse[0].content[0]) === null || _a === void 0 ? void 0 : _a.type) !== 'text') {
19335
- throw new PipelineExecutionError(`There is NOT 'text' BUT ${(_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type} finalMessages content type from OpenAI`);
19930
+ if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
19931
+ throw new PipelineExecutionError(`There is NOT 'text' BUT ${(_c = rawResponse[0].content[0]) === null || _c === void 0 ? void 0 : _c.type} finalMessages content type from OpenAI`);
19336
19932
  }
19337
- const resultContent = (_c = rawResponse[0].content[0]) === null || _c === void 0 ? void 0 : _c.text.value;
19933
+ const resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
19338
19934
  // <- TODO: [🧠] There are also annotations, maybe use them
19339
19935
  // eslint-disable-next-line prefer-const
19340
19936
  complete = $getCurrentDate();
@@ -19402,7 +19998,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19402
19998
  throw new NotAllowed(`Creating new assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
19403
19999
  }
19404
20000
  // await this.playground();
19405
- const { name, instructions, knowledgeSources } = options;
20001
+ const { name, instructions, knowledgeSources, tools } = options;
19406
20002
  const client = await this.getClient();
19407
20003
  let vectorStoreId;
19408
20004
  // If knowledge sources are provided, create a vector store with them
@@ -19473,7 +20069,11 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19473
20069
  description: 'Assistant created via Promptbook',
19474
20070
  model: 'gpt-4o',
19475
20071
  instructions,
19476
- tools: [/* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */ { type: 'file_search' }],
20072
+ tools: [
20073
+ /* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */
20074
+ { type: 'file_search' },
20075
+ ...(tools === undefined ? [] : mapToolsToOpenAi(tools)),
20076
+ ],
19477
20077
  };
19478
20078
  // Attach vector store if created
19479
20079
  if (vectorStoreId) {
@@ -19498,7 +20098,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19498
20098
  if (!this.isCreatingNewAssistantsAllowed) {
19499
20099
  throw new NotAllowed(`Updating assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
19500
20100
  }
19501
- const { assistantId, name, instructions, knowledgeSources } = options;
20101
+ const { assistantId, name, instructions, knowledgeSources, tools } = options;
19502
20102
  const client = await this.getClient();
19503
20103
  let vectorStoreId;
19504
20104
  // If knowledge sources are provided, create a vector store with them
@@ -19567,7 +20167,11 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19567
20167
  const assistantUpdate = {
19568
20168
  name,
19569
20169
  instructions,
19570
- tools: [/* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */ { type: 'file_search' }],
20170
+ tools: [
20171
+ /* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */
20172
+ { type: 'file_search' },
20173
+ ...(tools === undefined ? [] : mapToolsToOpenAi(tools)),
20174
+ ],
19571
20175
  };
19572
20176
  if (vectorStoreId) {
19573
20177
  assistantUpdate.tool_resources = {
@@ -19608,6 +20212,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19608
20212
  */
19609
20213
  const DISCRIMINANT = 'OPEN_AI_ASSISTANT_V1';
19610
20214
  /**
20215
+ * TODO: [🙎] In `OpenAiAssistantExecutionTools` Allow to create abstract assistants with `isCreatingNewAssistantsAllowed`
19611
20216
  * TODO: [🧠][🧙‍♂️] Maybe there can be some wizard for those who want to use just OpenAI
19612
20217
  * TODO: Maybe make custom OpenAiError
19613
20218
  * TODO: [🧠][🈁] Maybe use `isDeterministic` from options
@@ -19666,8 +20271,10 @@ class AgentLlmExecutionTools {
19666
20271
  }
19667
20272
  /**
19668
20273
  * Get cached or create agent model requirements
20274
+ *
20275
+ * Note: [🐤] This is names `getModelRequirements` *(not `getAgentModelRequirements`)* because in future these two will be united
19669
20276
  */
19670
- async getAgentModelRequirements() {
20277
+ async getModelRequirements() {
19671
20278
  if (this._cachedModelRequirements === null) {
19672
20279
  // Get available models from underlying LLM tools for best model selection
19673
20280
  const availableModels = await this.options.llmTools.listModels();
@@ -19737,9 +20344,25 @@ class AgentLlmExecutionTools {
19737
20344
  if (prompt.modelRequirements.modelVariant !== 'CHAT') {
19738
20345
  throw new Error('AgentLlmExecutionTools only supports chat prompts');
19739
20346
  }
19740
- const modelRequirements = await this.getAgentModelRequirements();
20347
+ const modelRequirements = await this.getModelRequirements();
19741
20348
  const chatPrompt = prompt;
19742
20349
  let underlyingLlmResult;
20350
+ // Create modified chat prompt with agent system message
20351
+ const promptWithAgentModelRequirements = {
20352
+ ...chatPrompt,
20353
+ modelRequirements: {
20354
+ ...chatPrompt.modelRequirements,
20355
+ ...modelRequirements,
20356
+ // Spread tools to convert readonly array to mutable
20357
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : chatPrompt.modelRequirements.tools,
20358
+ // Prepend agent system message to existing system message
20359
+ systemMessage: modelRequirements.systemMessage +
20360
+ (chatPrompt.modelRequirements.systemMessage
20361
+ ? `\n\n${chatPrompt.modelRequirements.systemMessage}`
20362
+ : ''),
20363
+ },
20364
+ };
20365
+ console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
19743
20366
  if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
19744
20367
  const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
19745
20368
  const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
@@ -19760,6 +20383,7 @@ class AgentLlmExecutionTools {
19760
20383
  name: this.title,
19761
20384
  instructions: modelRequirements.systemMessage,
19762
20385
  knowledgeSources: modelRequirements.knowledgeSources,
20386
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
19763
20387
  });
19764
20388
  AgentLlmExecutionTools.assistantCache.set(this.title, {
19765
20389
  assistantId: assistant.assistantId,
@@ -19776,6 +20400,7 @@ class AgentLlmExecutionTools {
19776
20400
  name: this.title,
19777
20401
  instructions: modelRequirements.systemMessage,
19778
20402
  knowledgeSources: modelRequirements.knowledgeSources,
20403
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
19779
20404
  /*
19780
20405
  !!!
19781
20406
  metadata: {
@@ -19788,32 +20413,28 @@ class AgentLlmExecutionTools {
19788
20413
  requirementsHash,
19789
20414
  });
19790
20415
  }
19791
- underlyingLlmResult = await assistant.callChatModelStream(chatPrompt, onProgress);
20416
+ // Create modified chat prompt with agent system message specific to OpenAI Assistant
20417
+ const promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools = {
20418
+ ...promptWithAgentModelRequirements,
20419
+ modelRequirements: {
20420
+ ...promptWithAgentModelRequirements.modelRequirements,
20421
+ modelName: undefined,
20422
+ systemMessage: undefined,
20423
+ temperature: undefined, // <- Note: Let the Assistant use its default temperature
20424
+ },
20425
+ };
20426
+ console.log('!!!! promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools:', promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools);
20427
+ underlyingLlmResult = await assistant.callChatModelStream(promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools, onProgress);
19792
20428
  }
19793
20429
  else {
19794
20430
  if (this.options.isVerbose) {
19795
20431
  console.log(`2️⃣ Creating Assistant ${this.title} on generic LLM execution tools...`);
19796
20432
  }
19797
- // Create modified chat prompt with agent system message
19798
- const modifiedChatPrompt = {
19799
- ...chatPrompt,
19800
- modelRequirements: {
19801
- ...chatPrompt.modelRequirements,
19802
- ...modelRequirements,
19803
- // Spread tools to convert readonly array to mutable
19804
- tools: modelRequirements.tools ? [...modelRequirements.tools] : chatPrompt.modelRequirements.tools,
19805
- // Prepend agent system message to existing system message
19806
- systemMessage: modelRequirements.systemMessage +
19807
- (chatPrompt.modelRequirements.systemMessage
19808
- ? `\n\n${chatPrompt.modelRequirements.systemMessage}`
19809
- : ''),
19810
- },
19811
- };
19812
20433
  if (this.options.llmTools.callChatModelStream) {
19813
- underlyingLlmResult = await this.options.llmTools.callChatModelStream(modifiedChatPrompt, onProgress);
20434
+ underlyingLlmResult = await this.options.llmTools.callChatModelStream(promptWithAgentModelRequirements, onProgress);
19814
20435
  }
19815
20436
  else if (this.options.llmTools.callChatModel) {
19816
- underlyingLlmResult = await this.options.llmTools.callChatModel(modifiedChatPrompt);
20437
+ underlyingLlmResult = await this.options.llmTools.callChatModel(promptWithAgentModelRequirements);
19817
20438
  onProgress(underlyingLlmResult);
19818
20439
  }
19819
20440
  else {
@@ -19842,7 +20463,8 @@ AgentLlmExecutionTools.assistantCache = new Map();
19842
20463
  * TODO: [🧠] Adding parameter substitution support (here or should be responsibility of the underlying LLM Tools)
19843
20464
  */
19844
20465
 
19845
- var _Agent_instances, _Agent_selfLearn;
20466
+ var _Agent_instances, _Agent_selfLearnSamples;
20467
+ // !!!!! import { RemoteAgent } from './RemoteAgent'; // <- [💞] <- !!!!!
19846
20468
  /**
19847
20469
  * Represents one AI Agent
19848
20470
  *
@@ -19902,6 +20524,10 @@ class Agent extends AgentLlmExecutionTools {
19902
20524
  * This is parsed from commitments like USE BROWSER, USE SEARCH ENGINE, KNOWLEDGE, etc.
19903
20525
  */
19904
20526
  this.capabilities = [];
20527
+ /**
20528
+ * List of sample conversations (question/answer pairs)
20529
+ */
20530
+ this.samples = [];
19905
20531
  /**
19906
20532
  * Metadata like image or color
19907
20533
  */
@@ -19911,12 +20537,13 @@ class Agent extends AgentLlmExecutionTools {
19911
20537
  this.agentSource = agentSource;
19912
20538
  this.agentSource.subscribe((source) => {
19913
20539
  this.updateAgentSource(source);
19914
- const { agentName, personaDescription, initialMessage, links, meta, capabilities } = parseAgentSource(source);
20540
+ const { agentName, personaDescription, initialMessage, links, meta, capabilities, samples } = parseAgentSource(source);
19915
20541
  this._agentName = agentName;
19916
20542
  this.personaDescription = personaDescription;
19917
20543
  this.initialMessage = initialMessage;
19918
20544
  this.links = links;
19919
20545
  this.capabilities = capabilities;
20546
+ this.samples = samples;
19920
20547
  this.meta = { ...this.meta, ...meta };
19921
20548
  });
19922
20549
  }
@@ -19928,10 +20555,10 @@ class Agent extends AgentLlmExecutionTools {
19928
20555
  async callChatModelStream(prompt, onProgress) {
19929
20556
  var _a;
19930
20557
  // [1] Check if the user is asking the same thing as in the samples
19931
- const modelRequirements = await this.getAgentModelRequirements();
20558
+ const modelRequirements = await this.getModelRequirements();
19932
20559
  if (modelRequirements.samples) {
19933
20560
  const normalizedPrompt = normalizeMessageText(prompt.content);
19934
- const sample = modelRequirements.samples.find((sample) => normalizeMessageText(sample.question) === normalizedPrompt);
20561
+ const sample = modelRequirements.samples.find((sample) => sample.question !== null && normalizeMessageText(sample.question) === normalizedPrompt);
19935
20562
  if (sample) {
19936
20563
  const now = new Date().toISOString();
19937
20564
  const result = {
@@ -19977,17 +20604,22 @@ class Agent extends AgentLlmExecutionTools {
19977
20604
  if ((_a = modelRequirements.metadata) === null || _a === void 0 ? void 0 : _a.isClosed) {
19978
20605
  return result;
19979
20606
  }
19980
- await __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_selfLearn).call(this, prompt, result);
19981
- // <- TODO: !!!! Do not await self-learn, run in background with error handling
20607
+ // TODO: !!!!! Is this timed properly?
20608
+ // Note: [1] Do the append of the samples
20609
+ __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_selfLearnSamples).call(this, prompt, result);
20610
+ /*
20611
+ !!!!!
20612
+ // Note: [2] Asynchronously call the teacher agent and invoke the silver link. When the teacher fails, keep just the samples
20613
+ this.#selfLearnTeacher(prompt, result).catch((error) => {
20614
+ if (this.options.isVerbose) {
20615
+ console.error('Failed to self-learn from teacher agent', error);
20616
+ }
20617
+ });
20618
+ */
19982
20619
  return result;
19983
20620
  }
19984
20621
  }
19985
- _Agent_instances = new WeakSet(), _Agent_selfLearn =
19986
- /**
19987
- * Self-learning: Appends the conversation and extracted knowledge to the agent source
19988
- */
19989
- async function _Agent_selfLearn(prompt, result) {
19990
- // Learning: Append the conversation sample to the agent source
20622
+ _Agent_instances = new WeakSet(), _Agent_selfLearnSamples = function _Agent_selfLearnSamples(prompt, result) {
19991
20623
  const learningExample = spaceTrim$2((block) => `
19992
20624
 
19993
20625
  ---
@@ -19999,52 +20631,9 @@ async function _Agent_selfLearn(prompt, result) {
19999
20631
  ${block(result.content)}
20000
20632
 
20001
20633
  `);
20002
- // Extract knowledge
20003
- let knowledgeBlock = '';
20004
- try {
20005
- const extractionPrompt = {
20006
- title: 'Knowledge Extraction',
20007
- modelRequirements: {
20008
- modelVariant: 'CHAT',
20009
- },
20010
- content: spaceTrim$2((block) => `
20011
- You are an AI agent that is learning from a conversation.
20012
-
20013
- Here is the conversation so far:
20014
-
20015
- User: ${block(prompt.content)}
20016
- Agent: ${block(result.content)}
20017
-
20018
- Extract any new knowledge, facts, or important information that should be remembered for future interactions.
20019
- Format the output as a list of KNOWLEDGE blocks.
20020
- If there is no new knowledge, return nothing.
20021
-
20022
- Example output:
20023
- KNOWLEDGE The user's name is Alice.
20024
- KNOWLEDGE The project deadline is next Friday.
20025
- `),
20026
- pipelineUrl: 'https://github.com/webgptorg/promptbook/blob/main/prompts/knowledge-extraction.ptbk.md',
20027
- parameters: {},
20028
- };
20029
- if (this.options.llmTools.callChatModel) {
20030
- const extractionResult = await this.options.llmTools.callChatModel(extractionPrompt);
20031
- const extractedContent = extractionResult.content;
20032
- if (extractedContent.includes('KNOWLEDGE')) {
20033
- knowledgeBlock = '\n\n' + spaceTrim$2(extractedContent);
20034
- }
20035
- }
20036
- else {
20037
- // TODO: [🧠] Fallback to callChatModelStream if callChatModel is not available
20038
- }
20039
- }
20040
- catch (error) {
20041
- if (this.options.isVerbose) {
20042
- console.warn('Failed to extract knowledge', error);
20043
- }
20044
- }
20045
20634
  // Append to the current source
20046
20635
  const currentSource = this.agentSource.value;
20047
- const newSource = padBook(validateBook(spaceTrim$2(currentSource) + '\n\n' + learningExample + knowledgeBlock));
20636
+ const newSource = padBook(validateBook(spaceTrim$2(currentSource) + '\n\n' + learningExample));
20048
20637
  // Update the source (which will trigger the subscription and update the underlying tools)
20049
20638
  this.agentSource.next(newSource);
20050
20639
  };
@@ -21286,7 +21875,7 @@ const OpenAiSdkTranspiler = {
21286
21875
  {
21287
21876
  role: 'system',
21288
21877
  content: spaceTrim(\`
21289
- ${block(modelRequirements.systemMessage)}
21878
+ ${block(modelRequirements.systemMessage.split('`').join('\\`'))}
21290
21879
  \`),
21291
21880
  },
21292
21881
  ];
@@ -21372,7 +21961,7 @@ const OpenAiSdkTranspiler = {
21372
21961
  {
21373
21962
  role: 'system',
21374
21963
  content: spaceTrim(\`
21375
- ${block(modelRequirements.systemMessage)}
21964
+ ${block(modelRequirements.systemMessage.split('`').join('\\`'))}
21376
21965
  \`),
21377
21966
  },
21378
21967
  ];
@@ -21433,7 +22022,16 @@ const CORE_AGENTS_SERVER = {
21433
22022
  *
21434
22023
  * @public exported from `@promptbook/core`
21435
22024
  */
21436
- const CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES = { ADAM: 'adam', TEACHER: 'teacher' };
22025
+ const CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES = {
22026
+ /**
22027
+ * The default ancestor agent for new agents
22028
+ */
22029
+ ADAM: 'adam',
22030
+ /**
22031
+ * Agent that knows book syntax and can help with self-learning
22032
+ */
22033
+ TEACHER: 'teacher',
22034
+ };
21437
22035
  // <- TODO: [🆎] Allow to override (set) well-known agent names via Metadata
21438
22036
  /**
21439
22037
  * Available agents servers for the Promptbook
@@ -21933,5 +22531,5 @@ function $generateBookBoilerplate(options) {
21933
22531
  * TODO: [🤶] Maybe export through `@promptbook/utils` or `@promptbook/random` package
21934
22532
  */
21935
22533
 
21936
- export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, CLAIM, CLI_APP_ID, CORE_AGENTS_SERVER, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, DEFAULT_BOOK, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NonTaskSectionTypes, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createAgentModelRequirementsWithCommitments, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getCommitmentDefinition, getGroupedCommitmentDefinitions, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, padBook, parseAgentSource, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
22534
+ export { $bookTranspilersRegister, $generateBookBoilerplate, $llmToolsMetadataRegister, $llmToolsRegister, $scrapersMetadataRegister, $scrapersRegister, ADMIN_EMAIL, ADMIN_GITHUB_NAME, API_REQUEST_TIMEOUT, AbstractFormatError, Agent, AgentCollectionInSupabase, AgentLlmExecutionTools, AuthenticationError, BIG_DATASET_TRESHOLD, BOOK_LANGUAGE_VERSION, BlackholeStorage, BoilerplateError, BoilerplateFormfactorDefinition, CLAIM, CLI_APP_ID, CORE_AGENTS_SERVER, CORE_AGENTS_SERVER_WELL_KNOWN_AGENT_NAMES, CallbackInterfaceTools, ChatbotFormfactorDefinition, CollectionError, CompletionFormfactorDefinition, CsvFormatError, CsvFormatParser, DEFAULT_AGENTS_DIRNAME, DEFAULT_BOOK, DEFAULT_BOOKS_DIRNAME, DEFAULT_BOOK_OUTPUT_PARAMETER_NAME, DEFAULT_BOOK_TITLE, DEFAULT_CSV_SETTINGS, DEFAULT_DOWNLOAD_CACHE_DIRNAME, DEFAULT_EXECUTION_CACHE_DIRNAME, DEFAULT_GET_PIPELINE_COLLECTION_FUNCTION_NAME, DEFAULT_INTERMEDIATE_FILES_STRATEGY, DEFAULT_IS_AUTO_INSTALLED, DEFAULT_IS_VERBOSE, DEFAULT_MAX_EXECUTION_ATTEMPTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_DEPTH, DEFAULT_MAX_KNOWLEDGE_SOURCES_SCRAPING_TOTAL, DEFAULT_MAX_PARALLEL_COUNT, DEFAULT_MAX_RECURSION, DEFAULT_MAX_REQUESTS_PER_MINUTE, DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME, DEFAULT_PROMPT_TASK_TITLE, DEFAULT_REMOTE_SERVER_URL, DEFAULT_SCRAPE_CACHE_DIRNAME, DEFAULT_TASK_SIMULATED_DURATION_MS, DEFAULT_TASK_TITLE, DatabaseError, EXPECTATION_UNITS, EnvironmentMismatchError, ExecutionReportStringOptionsDefaults, ExpectError, FAILED_VALUE_PLACEHOLDER, FORMFACTOR_DEFINITIONS, FormattedBookInMarkdownTranspiler, GENERIC_PIPELINE_INTERFACE, GeneratorFormfactorDefinition, GenericFormfactorDefinition, ImageGeneratorFormfactorDefinition, KnowledgeScrapeError, LimitReachedError, MANDATORY_CSV_SETTINGS, MAX_FILENAME_LENGTH, MODEL_ORDERS, MODEL_TRUST_LEVELS, MODEL_VARIANTS, MatcherFormfactorDefinition, MemoryStorage, MissingToolsError, MultipleLlmExecutionTools, NAME, NonTaskSectionTypes, NotAllowed, NotFoundError, NotYetImplementedCommitmentDefinition, NotYetImplementedError, ORDER_OF_PIPELINE_JSON, OpenAiSdkTranspiler, PADDING_LINES, PENDING_VALUE_PLACEHOLDER, PLAYGROUND_APP_ID, PROMPTBOOK_CHAT_COLOR, PROMPTBOOK_COLOR, PROMPTBOOK_ENGINE_VERSION, PROMPTBOOK_ERRORS, PROMPTBOOK_LEGAL_ENTITY, PROMPTBOOK_LOGO_URL, PROMPTBOOK_SYNTAX_COLORS, PUBLIC_AGENTS_SERVERS, ParseError, PipelineExecutionError, PipelineLogicError, PipelineUrlError, PrefixStorage, PromptbookFetchError, RESERVED_PARAMETER_NAMES, RemoteAgent, SET_IS_VERBOSE, SectionTypes, SheetsFormfactorDefinition, TaskTypes, TextFormatParser, TranslatorFormfactorDefinition, UNCERTAIN_USAGE, UNCERTAIN_ZERO_VALUE, USER_CHAT_COLOR, UnexpectedError, WrappedError, ZERO_USAGE, ZERO_VALUE, _AgentMetadata, _AgentRegistration, _AnthropicClaudeMetadataRegistration, _AzureOpenAiMetadataRegistration, _BoilerplateScraperMetadataRegistration, _DeepseekMetadataRegistration, _DocumentScraperMetadataRegistration, _GoogleMetadataRegistration, _LegacyDocumentScraperMetadataRegistration, _MarkdownScraperMetadataRegistration, _MarkitdownScraperMetadataRegistration, _OllamaMetadataRegistration, _OpenAiAssistantMetadataRegistration, _OpenAiCompatibleMetadataRegistration, _OpenAiMetadataRegistration, _PdfScraperMetadataRegistration, _WebsiteScraperMetadataRegistration, aboutPromptbookInformation, addUsage, book, cacheLlmTools, compilePipeline, computeAgentHash, computeCosineSimilarity, countUsage, createAgentLlmExecutionTools, createAgentModelRequirements, createAgentModelRequirementsWithCommitments, createBasicAgentModelRequirements, createDefaultAgentName, createEmptyAgentModelRequirements, createLlmToolsFromConfiguration, createPipelineCollectionFromJson, createPipelineCollectionFromPromise, createPipelineCollectionFromUrl, createPipelineExecutor, createPipelineSubcollection, embeddingVectorToString, executionReportJsonToString, extractParameterNamesFromTask, filterModels, generatePlaceholderAgentProfileImageUrl, getAllCommitmentDefinitions, getAllCommitmentTypes, getAllCommitmentsToolFunctions, getCommitmentDefinition, getGroupedCommitmentDefinitions, getPipelineInterface, getSingleLlmExecutionTools, identificationToPromptbookToken, isCommitmentSupported, isPassingExpectations, isPipelineImplementingInterface, isPipelineInterfacesEqual, isPipelinePrepared, isValidBook, isValidPipelineString, joinLlmExecutionTools, limitTotalUsage, makeKnowledgeSourceHandler, migratePipeline, normalizeAgentName, padBook, parseAgentSource, parseParameters, parsePipeline, pipelineCollectionToJson, pipelineJsonToString, prepareKnowledgePieces, preparePersona, preparePipeline, prettifyPipelineString, promptbookFetch, promptbookTokenToIdentification, unpreparePipeline, usageToHuman, usageToWorktime, validateBook, validatePipeline, validatePipelineString };
21937
22535
  //# sourceMappingURL=index.es.js.map