@promptbook/components 0.105.0-1 → 0.105.0-3

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 (39) hide show
  1. package/esm/index.es.js +840 -243
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/typings/servers.d.ts +6 -0
  4. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  5. package/esm/typings/src/_packages/types.index.d.ts +4 -0
  6. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +10 -3
  7. package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +11 -1
  8. package/esm/typings/src/book-2.0/agent-source/communication-samples.test.d.ts +1 -0
  9. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.blocks.test.d.ts +1 -0
  10. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.import.test.d.ts +1 -0
  11. package/esm/typings/src/book-2.0/agent-source/parseAgentSource.import.test.d.ts +1 -0
  12. package/esm/typings/src/book-2.0/agent-source/parseAgentSourceWithCommitments.blocks.test.d.ts +1 -0
  13. package/esm/typings/src/commitments/USE_TIME/USE_TIME.d.ts +40 -0
  14. package/esm/typings/src/commitments/USE_TIME/USE_TIME.test.d.ts +1 -0
  15. package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +8 -0
  16. package/esm/typings/src/commitments/_base/CommitmentDefinition.d.ts +8 -0
  17. package/esm/typings/src/commitments/index.d.ts +11 -2
  18. package/esm/typings/src/config.d.ts +1 -0
  19. package/esm/typings/src/import-plugins/$fileImportPlugins.d.ts +7 -0
  20. package/esm/typings/src/import-plugins/AgentFileImportPlugin.d.ts +7 -0
  21. package/esm/typings/src/import-plugins/FileImportPlugin.d.ts +24 -0
  22. package/esm/typings/src/import-plugins/JsonFileImportPlugin.d.ts +7 -0
  23. package/esm/typings/src/import-plugins/TextFileImportPlugin.d.ts +7 -0
  24. package/esm/typings/src/llm-providers/_common/utils/cache/cacheLlmTools.d.ts +2 -1
  25. package/esm/typings/src/llm-providers/_common/utils/count-total-usage/countUsage.d.ts +2 -2
  26. package/esm/typings/src/llm-providers/agent/Agent.d.ts +9 -2
  27. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +3 -1
  28. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +10 -0
  29. package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -1
  30. package/esm/typings/src/scripting/javascript/JavascriptExecutionToolsOptions.d.ts +6 -1
  31. package/esm/typings/src/types/ModelRequirements.d.ts +6 -12
  32. package/esm/typings/src/utils/execCommand/$execCommandNormalizeOptions.d.ts +2 -3
  33. package/esm/typings/src/utils/execCommand/ExecCommandOptions.d.ts +7 -1
  34. package/esm/typings/src/utils/organization/keepImported.d.ts +9 -0
  35. package/esm/typings/src/utils/organization/keepTypeImported.d.ts +0 -1
  36. package/esm/typings/src/version.d.ts +1 -1
  37. package/package.json +1 -1
  38. package/umd/index.umd.js +840 -243
  39. package/umd/index.umd.js.map +1 -1
package/esm/index.es.js CHANGED
@@ -35,7 +35,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
35
35
  * @generated
36
36
  * @see https://github.com/webgptorg/promptbook
37
37
  */
38
- const PROMPTBOOK_ENGINE_VERSION = '0.105.0-1';
38
+ const PROMPTBOOK_ENGINE_VERSION = '0.105.0-3';
39
39
  /**
40
40
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
41
41
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1106,6 +1106,7 @@ const PROMPTBOOK_SYNTAX_COLORS = {
1106
1106
  SEPARATOR: Color.fromHex('#cccccc'),
1107
1107
  COMMITMENT: Color.fromHex('#DA0F78'),
1108
1108
  PARAMETER: Color.fromHex('#8e44ad'),
1109
+ CODE_BLOCK: Color.fromHex('#7700ffff'),
1109
1110
  };
1110
1111
  // <- TODO: [🧠][🈵] Using `Color` here increases the package size approx 3kb, maybe remove it
1111
1112
  /**
@@ -3714,6 +3715,14 @@ class BaseCommitmentDefinition {
3714
3715
  return this.appendToSystemMessage(requirements, commentSection);
3715
3716
  }
3716
3717
  }
3718
+ /**
3719
+ * Gets tool function implementations provided by this commitment
3720
+ *
3721
+ * When the `applyToAgentModelRequirements` adds tools to the requirements, this method should return the corresponding function definitions.
3722
+ */
3723
+ getToolFunctions() {
3724
+ return {};
3725
+ }
3717
3726
  }
3718
3727
 
3719
3728
  /**
@@ -4346,79 +4355,6 @@ class FromCommitmentDefinition extends BaseCommitmentDefinition {
4346
4355
  * Note: [💞] Ignore a discrepancy between file name and entity name
4347
4356
  */
4348
4357
 
4349
- /**
4350
- * IMPORT commitment definition
4351
- *
4352
- * The IMPORT commitment tells the agent to import content from another agent at the current location.
4353
- *
4354
- * Example usage in agent source:
4355
- *
4356
- * ```book
4357
- * IMPORT https://s6.ptbk.io/benjamin-white
4358
- * ```
4359
- *
4360
- * @private [🪔] Maybe export the commitments through some package
4361
- */
4362
- class ImportCommitmentDefinition extends BaseCommitmentDefinition {
4363
- constructor(type = 'IMPORT') {
4364
- super(type);
4365
- }
4366
- /**
4367
- * Short one-line description of IMPORT.
4368
- */
4369
- get description() {
4370
- return 'Import content from another agent.';
4371
- }
4372
- /**
4373
- * Icon for this commitment.
4374
- */
4375
- get icon() {
4376
- return '📥';
4377
- }
4378
- /**
4379
- * Markdown documentation for IMPORT commitment.
4380
- */
4381
- get documentation() {
4382
- return spaceTrim$1(`
4383
- # ${this.type}
4384
-
4385
- Imports content from another agent at the location of the commitment.
4386
-
4387
- ## Examples
4388
-
4389
- \`\`\`book
4390
- My AI Agent
4391
-
4392
- IMPORT https://s6.ptbk.io/benjamin-white
4393
- RULE Speak only in English.
4394
- \`\`\`
4395
- `);
4396
- }
4397
- applyToAgentModelRequirements(requirements, content) {
4398
- const trimmedContent = content.trim();
4399
- if (!trimmedContent) {
4400
- return requirements;
4401
- }
4402
- if (!isValidAgentUrl(trimmedContent)) {
4403
- throw new Error(spaceTrim$1((block) => `
4404
- Invalid agent URL in IMPORT commitment: "${trimmedContent}"
4405
-
4406
- \`\`\`book
4407
- ${block(content)}
4408
- \`\`\`
4409
- `));
4410
- }
4411
- const importedAgentUrl = trimmedContent;
4412
- return {
4413
- ...requirements,
4414
- importedAgentUrls: [...(requirements.importedAgentUrls || []), importedAgentUrl],
4415
- };
4416
- }
4417
- }
4418
- /**
4419
- * Note: [💞] Ignore a discrepancy between file name and entity name
4420
- */
4421
-
4422
4358
  /**
4423
4359
  * GOAL commitment definition
4424
4360
  *
@@ -4519,6 +4455,87 @@ class GoalCommitmentDefinition extends BaseCommitmentDefinition {
4519
4455
  * Note: [💞] Ignore a discrepancy between file name and entity name
4520
4456
  */
4521
4457
 
4458
+ /**
4459
+ * IMPORT commitment definition
4460
+ *
4461
+ * The IMPORT commitment tells the agent to import content from another agent at the current location.
4462
+ *
4463
+ * Example usage in agent source:
4464
+ *
4465
+ * ```book
4466
+ * IMPORT https://s6.ptbk.io/benjamin-white
4467
+ * ```
4468
+ *
4469
+ * @private [🪔] Maybe export the commitments through some package
4470
+ */
4471
+ class ImportCommitmentDefinition extends BaseCommitmentDefinition {
4472
+ constructor(type = 'IMPORT') {
4473
+ super(type);
4474
+ }
4475
+ /**
4476
+ * Short one-line description of IMPORT.
4477
+ */
4478
+ get description() {
4479
+ return 'Import content from another agent or a generic text file.';
4480
+ }
4481
+ /**
4482
+ * Icon for this commitment.
4483
+ */
4484
+ get icon() {
4485
+ return '📥';
4486
+ }
4487
+ /**
4488
+ * Markdown documentation for IMPORT commitment.
4489
+ */
4490
+ get documentation() {
4491
+ return spaceTrim$1(`
4492
+ # ${this.type}
4493
+
4494
+ Imports content from another agent or a generic text file at the location of the commitment.
4495
+
4496
+ ## Examples
4497
+
4498
+ \`\`\`book
4499
+ My AI Agent
4500
+
4501
+ IMPORT https://s6.ptbk.io/benjamin-white
4502
+ IMPORT https://example.com/some-text-file.txt
4503
+ IMPORT ./path/to/local-file.json
4504
+ RULE Speak only in English.
4505
+ \`\`\`
4506
+ `);
4507
+ }
4508
+ applyToAgentModelRequirements(requirements, content) {
4509
+ const trimmedContent = content.trim();
4510
+ if (!trimmedContent) {
4511
+ return requirements;
4512
+ }
4513
+ if (isValidAgentUrl(trimmedContent)) {
4514
+ const importedAgentUrl = trimmedContent;
4515
+ return {
4516
+ ...requirements,
4517
+ importedAgentUrls: [...(requirements.importedAgentUrls || []), importedAgentUrl],
4518
+ };
4519
+ }
4520
+ if (isValidUrl(trimmedContent) || isValidFilePath(trimmedContent)) {
4521
+ return {
4522
+ ...requirements,
4523
+ importedFileUrls: [...(requirements.importedFileUrls || []), trimmedContent],
4524
+ };
4525
+ }
4526
+ throw new Error(spaceTrim$1((block) => `
4527
+ Invalid agent URL or file path in IMPORT commitment: "${trimmedContent}"
4528
+
4529
+ \`\`\`book
4530
+ ${block(content)}
4531
+ \`\`\`
4532
+ `));
4533
+ }
4534
+ }
4535
+ /**
4536
+ * Note: [💞] Ignore a discrepancy between file name and entity name
4537
+ */
4538
+
4522
4539
  /**
4523
4540
  * KNOWLEDGE commitment definition
4524
4541
  *
@@ -4938,7 +4955,13 @@ class InitialMessageCommitmentDefinition extends BaseCommitmentDefinition {
4938
4955
  `);
4939
4956
  }
4940
4957
  applyToAgentModelRequirements(requirements, content) {
4941
- return requirements;
4958
+ // INITIAL MESSAGE is for UI display purposes and for conversation history construction.
4959
+ const newSample = { question: null, answer: content };
4960
+ const newSamples = [...(requirements.samples || []), newSample];
4961
+ return {
4962
+ ...requirements,
4963
+ samples: newSamples,
4964
+ };
4942
4965
  }
4943
4966
  }
4944
4967
 
@@ -5970,27 +5993,16 @@ class NoteCommitmentDefinition extends BaseCommitmentDefinition {
5970
5993
  `);
5971
5994
  }
5972
5995
  applyToAgentModelRequirements(requirements, content) {
5973
- var _a;
5974
5996
  // The NOTE commitment makes no changes to the system message or model requirements
5975
5997
  // It only stores the note content in metadata for documentation purposes
5976
- const trimmedContent = content.trim();
5977
- if (!trimmedContent) {
5998
+ const trimmedContent = spaceTrim$1(content);
5999
+ if (trimmedContent === '') {
5978
6000
  return requirements;
5979
6001
  }
5980
- // Get existing note content from metadata
5981
- const existingNoteContent = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.NOTE) || '';
5982
- // Merge the new content with existing note content
5983
- // When multiple NOTE commitments exist, they are aggregated together
5984
- const mergedNoteContent = existingNoteContent ? `${existingNoteContent}\n${trimmedContent}` : trimmedContent;
5985
- // Store the merged note content in metadata for debugging and inspection
5986
- const updatedMetadata = {
5987
- ...requirements.metadata,
5988
- NOTE: mergedNoteContent,
5989
- };
5990
- // Return requirements with updated metadata but no changes to system message
6002
+ // Return requirements with updated notes but no changes to system message
5991
6003
  return {
5992
6004
  ...requirements,
5993
- metadata: updatedMetadata,
6005
+ notes: [...(requirements.notes || []), trimmedContent],
5994
6006
  };
5995
6007
  }
5996
6008
  }
@@ -7009,6 +7021,104 @@ class UseSearchEngineCommitmentDefinition extends BaseCommitmentDefinition {
7009
7021
  * Note: [💞] Ignore a discrepancy between file name and entity name
7010
7022
  */
7011
7023
 
7024
+ /**
7025
+ * USE TIME commitment definition
7026
+ *
7027
+ * The `USE TIME` commitment indicates that the agent should be able to determine the current date and time.
7028
+ *
7029
+ * Example usage in agent source:
7030
+ *
7031
+ * ```book
7032
+ * USE TIME
7033
+ * ```
7034
+ *
7035
+ * @private [🪔] Maybe export the commitments through some package
7036
+ */
7037
+ class UseTimeCommitmentDefinition extends BaseCommitmentDefinition {
7038
+ constructor() {
7039
+ super('USE TIME', ['CURRENT TIME', 'TIME', 'DATE']);
7040
+ }
7041
+ /**
7042
+ * Short one-line description of USE TIME.
7043
+ */
7044
+ get description() {
7045
+ return 'Enable the agent to determine the current date and time.';
7046
+ }
7047
+ /**
7048
+ * Icon for this commitment.
7049
+ */
7050
+ get icon() {
7051
+ return '🕒';
7052
+ }
7053
+ /**
7054
+ * Markdown documentation for USE TIME commitment.
7055
+ */
7056
+ get documentation() {
7057
+ return spaceTrim$1(`
7058
+ # USE TIME
7059
+
7060
+ Enables the agent to determine the current date and time.
7061
+
7062
+ ## Key aspects
7063
+
7064
+ - This tool won't receive any input.
7065
+ - It outputs the current date and time as an ISO 8601 string.
7066
+ - Allows the agent to answer questions about the current time or date.
7067
+
7068
+ ## Examples
7069
+
7070
+ \`\`\`book
7071
+ Time-aware Assistant
7072
+
7073
+ PERSONA You are a helpful assistant who knows the current time.
7074
+ USE TIME
7075
+ \`\`\`
7076
+ `);
7077
+ }
7078
+ applyToAgentModelRequirements(requirements, content) {
7079
+ // Get existing tools array or create new one
7080
+ const existingTools = requirements.tools || [];
7081
+ // Add 'get_current_time' to tools if not already present
7082
+ const updatedTools = existingTools.some((tool) => tool.name === 'get_current_time')
7083
+ ? existingTools
7084
+ : [
7085
+ ...existingTools,
7086
+ {
7087
+ name: 'get_current_time',
7088
+ description: 'Get the current date and time in ISO 8601 format.',
7089
+ parameters: {
7090
+ type: 'object',
7091
+ properties: {},
7092
+ required: [],
7093
+ },
7094
+ },
7095
+ // <- TODO: !!!! define the function in LLM tools
7096
+ ];
7097
+ // Return requirements with updated tools and metadata
7098
+ return {
7099
+ ...requirements,
7100
+ tools: updatedTools,
7101
+ metadata: {
7102
+ ...requirements.metadata,
7103
+ },
7104
+ };
7105
+ }
7106
+ /**
7107
+ * Gets the `get_current_time` tool function implementation.
7108
+ */
7109
+ getToolFunctions() {
7110
+ return {
7111
+ async get_current_time() {
7112
+ console.log('!!!! [Tool] get_current_time called');
7113
+ return new Date().toISOString();
7114
+ },
7115
+ };
7116
+ }
7117
+ }
7118
+ /**
7119
+ * Note: [💞] Ignore a discrepancy between file name and entity name
7120
+ */
7121
+
7012
7122
  /**
7013
7123
  * Placeholder commitment definition for commitments that are not yet implemented
7014
7124
  *
@@ -7076,7 +7186,6 @@ class NotYetImplementedCommitmentDefinition extends BaseCommitmentDefinition {
7076
7186
  }
7077
7187
  }
7078
7188
 
7079
- // Import all commitment definition classes
7080
7189
  /**
7081
7190
  * Registry of all available commitment definitions
7082
7191
  * This array contains instances of all commitment definitions
@@ -7093,10 +7202,10 @@ const COMMITMENT_REGISTRY = [
7093
7202
  new MemoryCommitmentDefinition('MEMORIES'),
7094
7203
  new StyleCommitmentDefinition('STYLE'),
7095
7204
  new StyleCommitmentDefinition('STYLES'),
7096
- new RuleCommitmentDefinition('RULE'),
7097
7205
  new RuleCommitmentDefinition('RULES'),
7098
- new LanguageCommitmentDefinition('LANGUAGE'),
7206
+ new RuleCommitmentDefinition('RULE'),
7099
7207
  new LanguageCommitmentDefinition('LANGUAGES'),
7208
+ new LanguageCommitmentDefinition('LANGUAGE'),
7100
7209
  new SampleCommitmentDefinition('SAMPLE'),
7101
7210
  new SampleCommitmentDefinition('EXAMPLE'),
7102
7211
  new FormatCommitmentDefinition('FORMAT'),
@@ -7136,6 +7245,7 @@ const COMMITMENT_REGISTRY = [
7136
7245
  new ClosedCommitmentDefinition(),
7137
7246
  new UseBrowserCommitmentDefinition(),
7138
7247
  new UseSearchEngineCommitmentDefinition(),
7248
+ new UseTimeCommitmentDefinition(),
7139
7249
  new UseMcpCommitmentDefinition(),
7140
7250
  new UseCommitmentDefinition(),
7141
7251
  // Not yet implemented commitments (using placeholder)
@@ -7248,11 +7358,37 @@ function parseAgentSourceWithCommitments(agentSource) {
7248
7358
  let currentCommitment = null;
7249
7359
  // Process lines starting from after the agent name line
7250
7360
  const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
7361
+ let isInsideCodeBlock = false;
7251
7362
  for (let i = startIndex; i < lines.length; i++) {
7252
7363
  const line = lines[i];
7253
7364
  if (line === undefined) {
7254
7365
  continue;
7255
7366
  }
7367
+ const trimmedLine = line.trim();
7368
+ // Check if this line starts or ends a code block
7369
+ if (trimmedLine.startsWith('```')) {
7370
+ isInsideCodeBlock = !isInsideCodeBlock;
7371
+ if (currentCommitment) {
7372
+ // If we are inside a commitment, the code block is part of it
7373
+ currentCommitment.contentLines.push(line);
7374
+ }
7375
+ else {
7376
+ // If we are not inside a commitment, the code block is non-commitment
7377
+ nonCommitmentLines.push(line);
7378
+ }
7379
+ continue;
7380
+ }
7381
+ if (isInsideCodeBlock) {
7382
+ if (currentCommitment) {
7383
+ // If we are inside a commitment and a code block, the line is part of the commitment
7384
+ currentCommitment.contentLines.push(line);
7385
+ }
7386
+ else {
7387
+ // If we are inside a code block but not a commitment, the line is non-commitment
7388
+ nonCommitmentLines.push(line);
7389
+ }
7390
+ continue;
7391
+ }
7256
7392
  // Check if this line starts a new commitment
7257
7393
  let foundNewCommitment = false;
7258
7394
  for (const definition of COMMITMENT_REGISTRY) {
@@ -7426,7 +7562,24 @@ function parseAgentSource(agentSource) {
7426
7562
  const meta = {};
7427
7563
  const links = [];
7428
7564
  const capabilities = [];
7565
+ const samples = [];
7566
+ let pendingUserMessage = null;
7429
7567
  for (const commitment of parseResult.commitments) {
7568
+ if (commitment.type === 'INITIAL MESSAGE') {
7569
+ samples.push({ question: null, answer: commitment.content });
7570
+ continue;
7571
+ }
7572
+ if (commitment.type === 'USER MESSAGE') {
7573
+ pendingUserMessage = commitment.content;
7574
+ continue;
7575
+ }
7576
+ if (commitment.type === 'AGENT MESSAGE') {
7577
+ if (pendingUserMessage !== null) {
7578
+ samples.push({ question: pendingUserMessage, answer: commitment.content });
7579
+ pendingUserMessage = null;
7580
+ }
7581
+ continue;
7582
+ }
7430
7583
  if (commitment.type === 'USE BROWSER') {
7431
7584
  capabilities.push({
7432
7585
  type: 'browser',
@@ -7443,6 +7596,37 @@ function parseAgentSource(agentSource) {
7443
7596
  });
7444
7597
  continue;
7445
7598
  }
7599
+ if (commitment.type === 'USE TIME') {
7600
+ capabilities.push({
7601
+ type: 'time',
7602
+ label: 'Time',
7603
+ iconName: 'Clock',
7604
+ });
7605
+ continue;
7606
+ }
7607
+ if (commitment.type === 'IMPORT') {
7608
+ const content = spaceTrim$2(commitment.content).split('\n')[0] || '';
7609
+ let label = content;
7610
+ const iconName = 'Download';
7611
+ try {
7612
+ if (content.startsWith('http://') || content.startsWith('https://')) {
7613
+ const url = new URL(content);
7614
+ label = url.hostname.replace(/^www\./, '') + '.../' + url.pathname.split('/').pop();
7615
+ }
7616
+ else if (content.startsWith('./') || content.startsWith('../') || content.startsWith('/')) {
7617
+ label = content.split('/').pop() || content;
7618
+ }
7619
+ }
7620
+ catch (e) {
7621
+ // Invalid URL or path, keep default label
7622
+ }
7623
+ capabilities.push({
7624
+ type: 'knowledge',
7625
+ label,
7626
+ iconName,
7627
+ });
7628
+ continue;
7629
+ }
7446
7630
  if (commitment.type === 'KNOWLEDGE') {
7447
7631
  const content = spaceTrim$2(commitment.content).split('\n')[0] || '';
7448
7632
  let label = content;
@@ -7527,6 +7711,7 @@ function parseAgentSource(agentSource) {
7527
7711
  links,
7528
7712
  parameters,
7529
7713
  capabilities,
7714
+ samples,
7530
7715
  };
7531
7716
  }
7532
7717
  /**
@@ -8596,13 +8781,17 @@ function BookEditorMonaco(props) {
8596
8781
  // Register a new language
8597
8782
  monaco.languages.register({ id: BOOK_LANGUAGE_ID });
8598
8783
  const commitmentTypes = [...new Set(getAllCommitmentDefinitions().map(({ type }) => type))];
8599
- const commitmentRegex = new RegExp(`^(${commitmentTypes.map((type) => (type === 'META' ? 'META\\s+\\w+' : type)).join('|')})`);
8784
+ const commitmentRegex = new RegExp(`^\\s*(${commitmentTypes
8785
+ .sort((a, b) => b.length - a.length) // [1] Prefer longer commitments to avoid partial matching (e.g. LANGUAGES vs LANGUAGE)
8786
+ .map((type) => (type === 'META' ? 'META\\s+\\w+' : type.replace(/\s+/, '\\s+')))
8787
+ .join('|')})(?=\\s|$)`);
8600
8788
  // Note: Using a broad character set for Latin and Cyrillic to support international characters in parameters.
8601
8789
  // Monarch tokenizer does not support Unicode property escapes like \p{L}.
8602
8790
  const parameterRegex = /@([a-zA-Z0-9_á-žÁ-Žč-řČ-Řš-žŠ-Žа-яА-ЯёЁ]+)/;
8603
8791
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8604
8792
  const bookRules = [
8605
8793
  [/^---[-]*$/, ''],
8794
+ [/^```.*$/, 'code-block', '@codeblock'],
8606
8795
  [parameterRegex, 'parameter'],
8607
8796
  [/\{[^}]+\}/, 'parameter'],
8608
8797
  [commitmentRegex, 'commitment'],
@@ -8614,10 +8803,15 @@ function BookEditorMonaco(props) {
8614
8803
  root: [
8615
8804
  [/^\s*$/, 'empty'],
8616
8805
  [/^-*$/, 'line'],
8806
+ [/^```.*$/, 'code-block', '@codeblock'],
8617
8807
  [/^.*$/, 'title', '@body'],
8618
8808
  [commitmentRegex, 'commitment'],
8619
8809
  ],
8620
8810
  body: bookRules,
8811
+ codeblock: [
8812
+ [/^```.*$/, 'code-block', '@pop'],
8813
+ [/^.*$/, 'code-block'],
8814
+ ],
8621
8815
  },
8622
8816
  });
8623
8817
  // Register a completion item provider for the language
@@ -8659,6 +8853,10 @@ function BookEditorMonaco(props) {
8659
8853
  foreground: PROMPTBOOK_SYNTAX_COLORS.PARAMETER.toHex(),
8660
8854
  fontStyle: `italic`,
8661
8855
  },
8856
+ {
8857
+ token: 'code-block',
8858
+ foreground: PROMPTBOOK_SYNTAX_COLORS.CODE_BLOCK.toHex(),
8859
+ },
8662
8860
  ],
8663
8861
  colors: {
8664
8862
  'editor.scrollbarSlider.background': '#E0E0E0',
@@ -8715,12 +8913,35 @@ function BookEditorMonaco(props) {
8715
8913
  .${instanceClass} .monaco-editor .transparent-text {
8716
8914
  color: transparent !important;
8717
8915
  }
8916
+
8917
+ .${instanceClass} .monaco-editor .code-block-box {
8918
+ background-color: #f5f5f566;
8919
+ border-left: 1px solid ${PROMPTBOOK_SYNTAX_COLORS.CODE_BLOCK.toHex()};
8920
+ border-right: 1px solid ${PROMPTBOOK_SYNTAX_COLORS.CODE_BLOCK.toHex()};
8921
+ padding-left: ${Math.round(8 * zoomLevel)}px;
8922
+ padding-right: ${Math.round(8 * zoomLevel)}px;
8923
+ }
8924
+
8925
+ .${instanceClass} .monaco-editor .code-block-top {
8926
+ border-top: 1px solid ${PROMPTBOOK_SYNTAX_COLORS.CODE_BLOCK.toHex()};
8927
+ border-top-left-radius: ${Math.round(10 * zoomLevel)}px;
8928
+ border-top-right-radius: ${Math.round(10 * zoomLevel)}px;
8929
+ overflow: hidden;
8930
+ }
8931
+
8932
+ .${instanceClass} .monaco-editor .code-block-bottom {
8933
+ border-bottom: 1px solid ${PROMPTBOOK_SYNTAX_COLORS.CODE_BLOCK.toHex()};
8934
+ border-bottom-left-radius: ${Math.round(10 * zoomLevel)}px;
8935
+ border-bottom-right-radius: ${Math.round(10 * zoomLevel)}px;
8936
+ overflow: hidden;
8937
+ }
8718
8938
  `;
8719
8939
  return () => {
8720
8940
  // Note: Style is not removed on purpose to avoid flickering during development with fast refresh
8721
8941
  };
8722
8942
  }, [scaledLineHeight, scaledContentPaddingLeft, scaledVerticalLineLeft]);
8723
8943
  const decorationIdsRef = useRef([]);
8944
+ const codeBlockDecorationIdsRef = useRef([]);
8724
8945
  useEffect(() => {
8725
8946
  if (!editor || !monaco) {
8726
8947
  return;
@@ -8749,6 +8970,39 @@ function BookEditorMonaco(props) {
8749
8970
  });
8750
8971
  }
8751
8972
  decorationIdsRef.current = editor.deltaDecorations(decorationIdsRef.current, newDecorations);
8973
+ // Add decorations for code blocks
8974
+ const lines = text.split('\n');
8975
+ const codeBlockDecorations = [];
8976
+ let inCodeBlock = false;
8977
+ let codeBlockStartLine = 0;
8978
+ for (let i = 0; i < lines.length; i++) {
8979
+ const line = lines[i];
8980
+ if (line === null || line === void 0 ? void 0 : line.trim().startsWith('```')) {
8981
+ if (!inCodeBlock) {
8982
+ // Starting a code block
8983
+ inCodeBlock = true;
8984
+ codeBlockStartLine = i + 1; // 1-based line number
8985
+ }
8986
+ else {
8987
+ // Ending a code block
8988
+ inCodeBlock = false;
8989
+ const endLine = i + 1; // 1-based line number
8990
+ // Add decorations for each line in the code block
8991
+ for (let j = codeBlockStartLine; j <= endLine; j++) {
8992
+ const isFirst = j === codeBlockStartLine;
8993
+ const isLast = j === endLine;
8994
+ codeBlockDecorations.push({
8995
+ range: new monaco.Range(j, 1, j, 1),
8996
+ options: {
8997
+ isWholeLine: true,
8998
+ className: `code-block-box${isFirst ? ' code-block-top' : ''}${isLast ? ' code-block-bottom' : ''}`,
8999
+ },
9000
+ });
9001
+ }
9002
+ }
9003
+ }
9004
+ }
9005
+ codeBlockDecorationIdsRef.current = editor.deltaDecorations(codeBlockDecorationIdsRef.current, codeBlockDecorations);
8752
9006
  };
8753
9007
  updateDecorations();
8754
9008
  const changeListener = editor.onDidChangeModelContent(() => {
@@ -12716,74 +12970,90 @@ function addUsage(...usageItems) {
12716
12970
  * in real-time through an observable.
12717
12971
  *
12718
12972
  * @param llmTools - The LLM tools to be intercepted and tracked
12719
- * @returns An augmented version of the tools that includes usage tracking capabilities
12973
+ * @returns Full proxy of the tools with added usage tracking capabilities
12720
12974
  * @public exported from `@promptbook/core`
12721
12975
  */
12722
12976
  function countUsage(llmTools) {
12723
12977
  let totalUsage = ZERO_USAGE;
12724
12978
  const spending = new Subject();
12725
- const proxyTools = {
12726
- get title() {
12727
- return `${llmTools.title} (+usage)`;
12728
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
12729
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
12730
- },
12731
- get description() {
12732
- return `${llmTools.description} (+usage)`;
12733
- // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
12734
- // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
12735
- },
12736
- checkConfiguration() {
12737
- return /* not await */ llmTools.checkConfiguration();
12738
- },
12739
- listModels() {
12740
- return /* not await */ llmTools.listModels();
12741
- },
12742
- spending() {
12743
- return spending.asObservable();
12744
- },
12745
- getTotalUsage() {
12746
- // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
12747
- return totalUsage;
12979
+ // Create a Proxy to intercept all property access and ensure full proxying of all properties
12980
+ const proxyTools = new Proxy(llmTools, {
12981
+ get(target, prop, receiver) {
12982
+ // Handle title property
12983
+ if (prop === 'title') {
12984
+ return `${target.title} (+usage)`;
12985
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
12986
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
12987
+ }
12988
+ // Handle description property
12989
+ if (prop === 'description') {
12990
+ return `${target.description} (+usage)`;
12991
+ // <- TODO: [🧈] Maybe standartize the suffix when wrapping `LlmExecutionTools` up
12992
+ // <- TODO: [🧈][🧠] Does it make sense to suffix "(+usage)"?
12993
+ }
12994
+ // Handle spending method (new method added by this wrapper)
12995
+ if (prop === 'spending') {
12996
+ return () => {
12997
+ return spending.asObservable();
12998
+ };
12999
+ }
13000
+ // Handle getTotalUsage method (new method added by this wrapper)
13001
+ if (prop === 'getTotalUsage') {
13002
+ // <- Note: [🥫] Not using getter `get totalUsage` but `getTotalUsage` to allow this object to be proxied
13003
+ return () => {
13004
+ return totalUsage;
13005
+ };
13006
+ }
13007
+ // Handle callChatModel method with usage counting
13008
+ if (prop === 'callChatModel' && target.callChatModel !== undefined) {
13009
+ return async (prompt) => {
13010
+ // console.info('[🚕] callChatModel through countTotalUsage');
13011
+ const promptResult = await target.callChatModel(prompt);
13012
+ totalUsage = addUsage(totalUsage, promptResult.usage);
13013
+ spending.next(promptResult.usage);
13014
+ return promptResult;
13015
+ };
13016
+ }
13017
+ // Handle callCompletionModel method with usage counting
13018
+ if (prop === 'callCompletionModel' && target.callCompletionModel !== undefined) {
13019
+ return async (prompt) => {
13020
+ // console.info('[🚕] callCompletionModel through countTotalUsage');
13021
+ const promptResult = await target.callCompletionModel(prompt);
13022
+ totalUsage = addUsage(totalUsage, promptResult.usage);
13023
+ spending.next(promptResult.usage);
13024
+ return promptResult;
13025
+ };
13026
+ }
13027
+ // Handle callEmbeddingModel method with usage counting
13028
+ if (prop === 'callEmbeddingModel' && target.callEmbeddingModel !== undefined) {
13029
+ return async (prompt) => {
13030
+ // console.info('[🚕] callEmbeddingModel through countTotalUsage');
13031
+ const promptResult = await target.callEmbeddingModel(prompt);
13032
+ totalUsage = addUsage(totalUsage, promptResult.usage);
13033
+ spending.next(promptResult.usage);
13034
+ return promptResult;
13035
+ };
13036
+ }
13037
+ // Handle callImageGenerationModel method with usage counting
13038
+ if (prop === 'callImageGenerationModel' && target.callImageGenerationModel !== undefined) {
13039
+ return async (prompt) => {
13040
+ // console.info('[🚕] callImageGenerationModel through countTotalUsage');
13041
+ const promptResult = await target.callImageGenerationModel(prompt);
13042
+ totalUsage = addUsage(totalUsage, promptResult.usage);
13043
+ spending.next(promptResult.usage);
13044
+ return promptResult;
13045
+ };
13046
+ }
13047
+ // <- Note: [🤖]
13048
+ // For all other properties and methods, delegate to the original target
13049
+ const value = Reflect.get(target, prop, receiver);
13050
+ // If it's a function, bind it to the target to preserve context
13051
+ if (typeof value === 'function') {
13052
+ return value.bind(target);
13053
+ }
13054
+ return value;
12748
13055
  },
12749
- };
12750
- if (llmTools.callChatModel !== undefined) {
12751
- proxyTools.callChatModel = async (prompt) => {
12752
- // console.info('[🚕] callChatModel through countTotalUsage');
12753
- const promptResult = await llmTools.callChatModel(prompt);
12754
- totalUsage = addUsage(totalUsage, promptResult.usage);
12755
- spending.next(promptResult.usage);
12756
- return promptResult;
12757
- };
12758
- }
12759
- if (llmTools.callCompletionModel !== undefined) {
12760
- proxyTools.callCompletionModel = async (prompt) => {
12761
- // console.info('[🚕] callCompletionModel through countTotalUsage');
12762
- const promptResult = await llmTools.callCompletionModel(prompt);
12763
- totalUsage = addUsage(totalUsage, promptResult.usage);
12764
- spending.next(promptResult.usage);
12765
- return promptResult;
12766
- };
12767
- }
12768
- if (llmTools.callEmbeddingModel !== undefined) {
12769
- proxyTools.callEmbeddingModel = async (prompt) => {
12770
- // console.info('[🚕] callEmbeddingModel through countTotalUsage');
12771
- const promptResult = await llmTools.callEmbeddingModel(prompt);
12772
- totalUsage = addUsage(totalUsage, promptResult.usage);
12773
- spending.next(promptResult.usage);
12774
- return promptResult;
12775
- };
12776
- }
12777
- if (llmTools.callImageGenerationModel !== undefined) {
12778
- proxyTools.callImageGenerationModel = async (prompt) => {
12779
- // console.info('[🚕] callImageGenerationModel through countTotalUsage');
12780
- const promptResult = await llmTools.callImageGenerationModel(prompt);
12781
- totalUsage = addUsage(totalUsage, promptResult.usage);
12782
- spending.next(promptResult.usage);
12783
- return promptResult;
12784
- };
12785
- }
12786
- // <- Note: [🤖]
13056
+ });
12787
13057
  return proxyTools;
12788
13058
  }
12789
13059
  /**
@@ -15563,6 +15833,97 @@ function createBasicAgentModelRequirements(agentName) {
15563
15833
  * TODO: [🐤] Deduplicate `AgentModelRequirements` and `ModelRequirements` model requirements
15564
15834
  */
15565
15835
 
15836
+ /**
15837
+ * Plugin for importing agent books *(`.book` files)*
15838
+ *
15839
+ * @private [🥝] Maybe export the import plugins through some package
15840
+ */
15841
+ const AgentFileImportPlugin = {
15842
+ name: 'agent-file-import-plugin',
15843
+ canImport(mimeType) {
15844
+ // [🧠] Should we have a specific MIME type for agent books?
15845
+ // For now, let's assume it's identified by .book extension or certain MIME types if provided
15846
+ return mimeType === 'text/x-promptbook' || mimeType === 'application/x-promptbook';
15847
+ },
15848
+ import(content) {
15849
+ const parseResult = parseAgentSourceWithCommitments(content);
15850
+ // Bring only the agent corpus (non-commitment lines and relevant commitments)
15851
+ // Stripping the agent name (which is usually the first line)
15852
+ const corpus = parseResult.nonCommitmentLines
15853
+ .filter((line, index) => index > 0 || !parseResult.agentName)
15854
+ .join('\n')
15855
+ .trim();
15856
+ // Also include relevant commitments that make up the "corpus" of the agent
15857
+ // For example PERSONA, RULE, KNOWLEDGE
15858
+ const relevantCommitments = parseResult.commitments
15859
+ .filter((c) => ['PERSONA', 'RULE', 'KNOWLEDGE'].includes(c.type))
15860
+ .map((c) => `${c.type} ${c.content}`)
15861
+ .join('\n\n');
15862
+ return spaceTrim$1((block) => `
15863
+ ${block(relevantCommitments)}
15864
+
15865
+ ${block(corpus)}
15866
+ `).trim();
15867
+ },
15868
+ };
15869
+
15870
+ /**
15871
+ * Plugin for importing JSON files
15872
+ *
15873
+ * @private [🥝] Maybe export the import plugins through some package
15874
+ */
15875
+ const JsonFileImportPlugin = {
15876
+ name: 'json-file-import-plugin',
15877
+ canImport(mimeType) {
15878
+ return mimeType === 'application/json' || mimeType.endsWith('+json');
15879
+ },
15880
+ import(content) {
15881
+ try {
15882
+ const json = JSON.parse(content);
15883
+ const formattedJson = JSON.stringify(json, null, 4);
15884
+ return `\`\`\`json\n${formattedJson}\n\`\`\``;
15885
+ }
15886
+ catch (error) {
15887
+ // If JSON is invalid, still import it but maybe not as pretty JSON
15888
+ return `\`\`\`json\n${content}\n\`\`\``;
15889
+ }
15890
+ },
15891
+ };
15892
+
15893
+ /**
15894
+ * Plugin for importing generic text files
15895
+ *
15896
+ * @private [🥝] Maybe export the import plugins through some package
15897
+ */
15898
+ const TextFileImportPlugin = {
15899
+ name: 'text-file-import-plugin',
15900
+ canImport(mimeType) {
15901
+ return (mimeType === 'text/plain' ||
15902
+ mimeType === 'text/markdown' ||
15903
+ mimeType === 'text/x-typescript' ||
15904
+ mimeType === 'text/javascript' ||
15905
+ mimeType === 'text/css' ||
15906
+ mimeType === 'text/html' ||
15907
+ mimeType.startsWith('text/'));
15908
+ },
15909
+ import(content, mimeType) {
15910
+ const extension = mimeTypeToExtension(mimeType);
15911
+ const codeBlockType = extension || 'txt';
15912
+ return `\`\`\`${codeBlockType}\n${content}\n\`\`\``;
15913
+ },
15914
+ };
15915
+
15916
+ /**
15917
+ * All available file import plugins
15918
+ *
15919
+ * @private [🥝] Maybe export the import plugins through some package
15920
+ */
15921
+ const $fileImportPlugins = [
15922
+ AgentFileImportPlugin,
15923
+ JsonFileImportPlugin,
15924
+ TextFileImportPlugin,
15925
+ ];
15926
+
15566
15927
  /**
15567
15928
  * Removes comment lines (lines starting with #) from a system message
15568
15929
  * This is used to clean up the final system message before sending it to the AI model
@@ -15594,6 +15955,7 @@ function removeCommentsFromSystemMessage(systemMessage) {
15594
15955
  * @public exported from `@promptbook/core`
15595
15956
  */
15596
15957
  async function createAgentModelRequirementsWithCommitments(agentSource, modelName) {
15958
+ var _a;
15597
15959
  // Parse the agent source to extract commitments
15598
15960
  const parseResult = parseAgentSourceWithCommitments(agentSource);
15599
15961
  // Apply DELETE filtering: remove prior commitments tagged by parameters targeted by DELETE/CANCEL/DISCARD/REMOVE
@@ -15660,6 +16022,64 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
15660
16022
  }
15661
16023
  }
15662
16024
  }
16025
+ // Handle IMPORT commitments for generic files
16026
+ // Note: This logic could be moved to ImportCommitmentDefinition, but it needs to be asynchronous
16027
+ if (requirements.importedFileUrls && requirements.importedFileUrls.length > 0) {
16028
+ for (const fileUrl of requirements.importedFileUrls) {
16029
+ try {
16030
+ // 1. Mocked security check
16031
+ await mockedSecurityCheck(fileUrl);
16032
+ // 2. Fetch file content
16033
+ let content;
16034
+ let mimeType = null;
16035
+ if (isValidUrl(fileUrl)) {
16036
+ const response = await promptbookFetch(fileUrl);
16037
+ if (!response.ok) {
16038
+ throw new Error(`Failed to fetch ${fileUrl}: ${response.statusText}`);
16039
+ }
16040
+ content = await response.text();
16041
+ mimeType = response.headers.get('Content-Type');
16042
+ /*
16043
+ TODO: !!!! Commented out this case because we need to work in Browser-compatible mode in many packages, use passed `fs` instead
16044
+ } else if (isValidFilePath(fileUrl)) {
16045
+ // [x🟢x] This code is expected to run in Node environment if local files are used
16046
+ const fs = await import('fs/promises');
16047
+ content = await fs.readFile(fileUrl, 'utf-8');
16048
+ const extension = getFileExtension(fileUrl);
16049
+ mimeType = extensionToMimeType(extension as string);
16050
+ */
16051
+ }
16052
+ else {
16053
+ throw new Error(`Invalid file URL or path: ${fileUrl}`);
16054
+ }
16055
+ if (!mimeType) {
16056
+ mimeType = 'text/plain';
16057
+ }
16058
+ // Remove charset from mime type
16059
+ mimeType = mimeType.split(';')[0].trim();
16060
+ // 3. Prevent importing binary files (mocked check)
16061
+ if (isBinaryMimeType(mimeType)) {
16062
+ throw new Error(`Importing binary files is not allowed: ${mimeType}`);
16063
+ }
16064
+ // 4. Find appropriate plugin
16065
+ const plugin = $fileImportPlugins.find((p) => p.canImport(mimeType));
16066
+ if (!plugin) {
16067
+ throw new Error(`No import plugin found for MIME type: ${mimeType}`);
16068
+ }
16069
+ // 5. Process content
16070
+ const importedContent = await plugin.import(content, mimeType);
16071
+ // 6. Append to system message
16072
+ requirements = {
16073
+ ...requirements,
16074
+ systemMessage: requirements.systemMessage + '\n\n' + importedContent,
16075
+ };
16076
+ }
16077
+ catch (error) {
16078
+ console.warn(`Failed to import file ${fileUrl}:`, error);
16079
+ // Continue with other imports even if one fails
16080
+ }
16081
+ }
16082
+ }
15663
16083
  // Handle MCP servers (extract from original agent source)
15664
16084
  const mcpServers = extractMcpServers(agentSource);
15665
16085
  if (mcpServers.length > 0) {
@@ -15669,9 +16089,11 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
15669
16089
  };
15670
16090
  }
15671
16091
  // Add non-commitment lines to system message if they exist
16092
+ // Note: Filtering out horizontal lines (---) as requested
15672
16093
  const nonCommitmentContent = parseResult.nonCommitmentLines
15673
16094
  .filter((line, index) => index > 0 || !parseResult.agentName) // Skip first line if it's the agent name
15674
16095
  .filter((line) => line.trim()) // Remove empty lines
16096
+ .filter((line) => !/^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/.test(line)) // Remove horizontal lines
15675
16097
  .join('\n')
15676
16098
  .trim();
15677
16099
  if (nonCommitmentContent) {
@@ -15680,6 +16102,26 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
15680
16102
  systemMessage: requirements.systemMessage + '\n\n' + nonCommitmentContent,
15681
16103
  };
15682
16104
  }
16105
+ // Add example interactions to the system message
16106
+ const examples = [];
16107
+ // 1. Initial message as an example agent response
16108
+ const initialMessage = (_a = parseResult.commitments.find((c) => c.type === 'INITIAL MESSAGE')) === null || _a === void 0 ? void 0 : _a.content;
16109
+ if (initialMessage) {
16110
+ examples.push(`Agent: ${initialMessage}`);
16111
+ }
16112
+ // 2. User and Agent message pairs
16113
+ if (requirements.samples && requirements.samples.length > 0) {
16114
+ for (const sample of requirements.samples) {
16115
+ examples.push(`User: ${sample.question}\nAgent: ${sample.answer}`);
16116
+ }
16117
+ }
16118
+ if (examples.length > 0) {
16119
+ const exampleInteractionsContent = `Example interaction:\n\n${examples.join('\n\n')}`;
16120
+ requirements = {
16121
+ ...requirements,
16122
+ systemMessage: requirements.systemMessage + '\n\n' + exampleInteractionsContent,
16123
+ };
16124
+ }
15683
16125
  // Remove comment lines (lines starting with #) from the final system message
15684
16126
  // while preserving the original content with comments in metadata
15685
16127
  const cleanedSystemMessage = removeCommentsFromSystemMessage(requirements.systemMessage);
@@ -15688,6 +16130,36 @@ async function createAgentModelRequirementsWithCommitments(agentSource, modelNam
15688
16130
  systemMessage: cleanedSystemMessage,
15689
16131
  };
15690
16132
  }
16133
+ /**
16134
+ * Mocked security check for imported files
16135
+ *
16136
+ * @param urlOrPath - The URL or local path of the file to check
16137
+ * @returns A promise that resolves if the file is safe
16138
+ */
16139
+ async function mockedSecurityCheck(urlOrPath) {
16140
+ // TODO: Implement proper security checks
16141
+ await new Promise((resolve) => setTimeout(resolve, 10)); // Mock async delay
16142
+ if (urlOrPath.includes('malicious')) {
16143
+ throw new Error(`Security check failed for: ${urlOrPath}`);
16144
+ }
16145
+ }
16146
+ /**
16147
+ * Checks if the given MIME type belongs to a binary file
16148
+ *
16149
+ * @param mimeType - The MIME type to check
16150
+ * @returns True if it's a binary MIME type
16151
+ */
16152
+ function isBinaryMimeType(mimeType) {
16153
+ const binaryPrefixes = [
16154
+ 'image/',
16155
+ 'video/',
16156
+ 'audio/',
16157
+ 'application/octet-stream',
16158
+ 'application/pdf',
16159
+ 'application/zip',
16160
+ ];
16161
+ return binaryPrefixes.some((prefix) => mimeType.startsWith(prefix));
16162
+ }
15691
16163
 
15692
16164
  /**
15693
16165
  * Creates model requirements for an agent based on its source
@@ -17514,7 +17986,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17514
17986
  * Calls OpenAI API to use a chat model with streaming.
17515
17987
  */
17516
17988
  async callChatModelStream(prompt, onProgress) {
17517
- var _a, _b, _c;
17989
+ var _a, _b, _c, _d;
17518
17990
  if (this.options.isVerbose) {
17519
17991
  console.info('💬 OpenAI callChatModel call', { prompt });
17520
17992
  }
@@ -17569,6 +18041,131 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17569
18041
  }
17570
18042
  // Always add the current user message
17571
18043
  threadMessages.push({ role: 'user', content: rawPromptContent });
18044
+ // Check if tools are being used - if so, use non-streaming mode
18045
+ const hasTools = modelRequirements.tools !== undefined && modelRequirements.tools.length > 0;
18046
+ const start = $getCurrentDate();
18047
+ let complete;
18048
+ // [🐱‍🚀] When tools are present, we need to use the non-streaming Runs API
18049
+ // because streaming doesn't support tool execution flow properly
18050
+ if (hasTools) {
18051
+ const rawRequest = {
18052
+ assistant_id: this.assistantId,
18053
+ thread: {
18054
+ messages: threadMessages,
18055
+ },
18056
+ tools: mapToolsToOpenAi(modelRequirements.tools),
18057
+ };
18058
+ if (this.options.isVerbose) {
18059
+ console.info(colors.bgWhite('rawRequest (non-streaming with tools)'), JSON.stringify(rawRequest, null, 4));
18060
+ }
18061
+ // Create thread and run
18062
+ const threadAndRun = await client.beta.threads.createAndRun(rawRequest);
18063
+ let run = threadAndRun;
18064
+ // Poll until run completes or requires action
18065
+ while (run.status === 'queued' || run.status === 'in_progress' || run.status === 'requires_action') {
18066
+ if (run.status === 'requires_action' && ((_a = run.required_action) === null || _a === void 0 ? void 0 : _a.type) === 'submit_tool_outputs') {
18067
+ // Execute tools
18068
+ const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
18069
+ const toolOutputs = [];
18070
+ for (const toolCall of toolCalls) {
18071
+ if (toolCall.type === 'function') {
18072
+ const functionName = toolCall.function.name;
18073
+ const functionArgs = JSON.parse(toolCall.function.arguments);
18074
+ if (this.options.isVerbose) {
18075
+ console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
18076
+ }
18077
+ // Get execution tools for script execution
18078
+ const executionTools = this.options
18079
+ .executionTools;
18080
+ if (!executionTools || !executionTools.script) {
18081
+ throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
18082
+ }
18083
+ // TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
18084
+ const scriptTools = Array.isArray(executionTools.script)
18085
+ ? executionTools.script
18086
+ : [executionTools.script];
18087
+ let functionResponse;
18088
+ try {
18089
+ const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
18090
+ functionResponse = await scriptTool.execute({
18091
+ scriptLanguage: 'javascript',
18092
+ script: `
18093
+ const args = ${JSON.stringify(functionArgs)};
18094
+ return await ${functionName}(args);
18095
+ `,
18096
+ parameters: {}, // <- TODO: [🧠] What parameters to pass?
18097
+ });
18098
+ if (this.options.isVerbose) {
18099
+ console.info(`✅ Tool ${functionName} executed:`, functionResponse);
18100
+ }
18101
+ }
18102
+ catch (error) {
18103
+ assertsError(error);
18104
+ functionResponse = spaceTrim$2((block) => `
18105
+
18106
+ The invoked tool \`${functionName}\` failed with error:
18107
+
18108
+ \`\`\`json
18109
+ ${block(JSON.stringify(serializeError(error), null, 4))}
18110
+ \`\`\`
18111
+
18112
+ `);
18113
+ console.error(colors.bgRed(`❌ Error executing tool ${functionName}:`));
18114
+ console.error(error);
18115
+ }
18116
+ toolOutputs.push({
18117
+ tool_call_id: toolCall.id,
18118
+ output: functionResponse,
18119
+ });
18120
+ }
18121
+ }
18122
+ // Submit tool outputs
18123
+ run = await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
18124
+ tool_outputs: toolOutputs,
18125
+ });
18126
+ }
18127
+ else {
18128
+ // Wait a bit before polling again
18129
+ await new Promise((resolve) => setTimeout(resolve, 500));
18130
+ run = await client.beta.threads.runs.retrieve(run.thread_id, run.id);
18131
+ }
18132
+ }
18133
+ if (run.status !== 'completed') {
18134
+ throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
18135
+ }
18136
+ // Get messages from the thread
18137
+ const messages = await client.beta.threads.messages.list(run.thread_id);
18138
+ const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
18139
+ if (assistantMessages.length === 0) {
18140
+ throw new PipelineExecutionError('No assistant messages found after run completion');
18141
+ }
18142
+ const lastMessage = assistantMessages[0];
18143
+ const textContent = lastMessage.content.find((c) => c.type === 'text');
18144
+ if (!textContent || textContent.type !== 'text') {
18145
+ throw new PipelineExecutionError('No text content in assistant response');
18146
+ }
18147
+ complete = $getCurrentDate();
18148
+ const resultContent = textContent.text.value;
18149
+ const usage = UNCERTAIN_USAGE;
18150
+ // Progress callback with final result
18151
+ const finalChunk = {
18152
+ content: resultContent,
18153
+ modelName: 'assistant',
18154
+ timing: { start, complete },
18155
+ usage,
18156
+ rawPromptContent,
18157
+ rawRequest,
18158
+ rawResponse: { run, messages: messages.data },
18159
+ };
18160
+ onProgress(finalChunk);
18161
+ return exportJson({
18162
+ name: 'promptResult',
18163
+ message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
18164
+ order: [],
18165
+ value: finalChunk,
18166
+ });
18167
+ }
18168
+ // Streaming mode (without tools)
17572
18169
  const rawRequest = {
17573
18170
  // TODO: [👨‍👨‍👧‍👧] ...modelSettings,
17574
18171
  // TODO: [👨‍👨‍👧‍👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
@@ -17579,10 +18176,8 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17579
18176
  tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
17580
18177
  // <- TODO: Add user identification here> user: this.options.user,
17581
18178
  };
17582
- const start = $getCurrentDate();
17583
- let complete;
17584
18179
  if (this.options.isVerbose) {
17585
- console.info(colors.bgWhite('rawRequest'), JSON.stringify(rawRequest, null, 4));
18180
+ console.info(colors.bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
17586
18181
  }
17587
18182
  const stream = await client.beta.threads.createAndRunStream(rawRequest);
17588
18183
  stream.on('connect', () => {
@@ -17618,6 +18213,15 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17618
18213
  console.info('messageDone', message);
17619
18214
  }
17620
18215
  });
18216
+ // TODO: [🐱‍🚀] Handle tool calls in assistants
18217
+ // Note: OpenAI Assistant streaming with tool calls requires special handling.
18218
+ // The stream will pause when a tool call is needed, and we need to:
18219
+ // 1. Wait for the run to reach 'requires_action' status
18220
+ // 2. Execute the tool calls
18221
+ // 3. Submit tool outputs via a separate API call (not on the stream)
18222
+ // 4. Continue the run
18223
+ // This requires switching to non-streaming mode or using the Runs API directly.
18224
+ // For now, tools with assistants should use the non-streaming chat completions API instead.
17621
18225
  const rawResponse = await stream.finalMessages();
17622
18226
  if (this.options.isVerbose) {
17623
18227
  console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
@@ -17628,10 +18232,10 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17628
18232
  if (rawResponse[0].content.length !== 1) {
17629
18233
  throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
17630
18234
  }
17631
- if (((_a = rawResponse[0].content[0]) === null || _a === void 0 ? void 0 : _a.type) !== 'text') {
17632
- 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`);
18235
+ if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
18236
+ 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`);
17633
18237
  }
17634
- const resultContent = (_c = rawResponse[0].content[0]) === null || _c === void 0 ? void 0 : _c.text.value;
18238
+ const resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
17635
18239
  // <- TODO: [🧠] There are also annotations, maybe use them
17636
18240
  // eslint-disable-next-line prefer-const
17637
18241
  complete = $getCurrentDate();
@@ -17699,7 +18303,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17699
18303
  throw new NotAllowed(`Creating new assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
17700
18304
  }
17701
18305
  // await this.playground();
17702
- const { name, instructions, knowledgeSources } = options;
18306
+ const { name, instructions, knowledgeSources, tools } = options;
17703
18307
  const client = await this.getClient();
17704
18308
  let vectorStoreId;
17705
18309
  // If knowledge sources are provided, create a vector store with them
@@ -17770,7 +18374,11 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17770
18374
  description: 'Assistant created via Promptbook',
17771
18375
  model: 'gpt-4o',
17772
18376
  instructions,
17773
- tools: [/* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */ { type: 'file_search' }],
18377
+ tools: [
18378
+ /* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */
18379
+ { type: 'file_search' },
18380
+ ...(tools === undefined ? [] : mapToolsToOpenAi(tools)),
18381
+ ],
17774
18382
  };
17775
18383
  // Attach vector store if created
17776
18384
  if (vectorStoreId) {
@@ -17795,7 +18403,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17795
18403
  if (!this.isCreatingNewAssistantsAllowed) {
17796
18404
  throw new NotAllowed(`Updating assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
17797
18405
  }
17798
- const { assistantId, name, instructions, knowledgeSources } = options;
18406
+ const { assistantId, name, instructions, knowledgeSources, tools } = options;
17799
18407
  const client = await this.getClient();
17800
18408
  let vectorStoreId;
17801
18409
  // If knowledge sources are provided, create a vector store with them
@@ -17864,7 +18472,11 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17864
18472
  const assistantUpdate = {
17865
18473
  name,
17866
18474
  instructions,
17867
- tools: [/* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */ { type: 'file_search' }],
18475
+ tools: [
18476
+ /* TODO: [🧠] Maybe add { type: 'code_interpreter' }, */
18477
+ { type: 'file_search' },
18478
+ ...(tools === undefined ? [] : mapToolsToOpenAi(tools)),
18479
+ ],
17868
18480
  };
17869
18481
  if (vectorStoreId) {
17870
18482
  assistantUpdate.tool_resources = {
@@ -17905,6 +18517,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
17905
18517
  */
17906
18518
  const DISCRIMINANT = 'OPEN_AI_ASSISTANT_V1';
17907
18519
  /**
18520
+ * TODO: [🙎] In `OpenAiAssistantExecutionTools` Allow to create abstract assistants with `isCreatingNewAssistantsAllowed`
17908
18521
  * TODO: [🧠][🧙‍♂️] Maybe there can be some wizard for those who want to use just OpenAI
17909
18522
  * TODO: Maybe make custom OpenAiError
17910
18523
  * TODO: [🧠][🈁] Maybe use `isDeterministic` from options
@@ -17963,8 +18576,10 @@ class AgentLlmExecutionTools {
17963
18576
  }
17964
18577
  /**
17965
18578
  * Get cached or create agent model requirements
18579
+ *
18580
+ * Note: [🐤] This is names `getModelRequirements` *(not `getAgentModelRequirements`)* because in future these two will be united
17966
18581
  */
17967
- async getAgentModelRequirements() {
18582
+ async getModelRequirements() {
17968
18583
  if (this._cachedModelRequirements === null) {
17969
18584
  // Get available models from underlying LLM tools for best model selection
17970
18585
  const availableModels = await this.options.llmTools.listModels();
@@ -18034,9 +18649,25 @@ class AgentLlmExecutionTools {
18034
18649
  if (prompt.modelRequirements.modelVariant !== 'CHAT') {
18035
18650
  throw new Error('AgentLlmExecutionTools only supports chat prompts');
18036
18651
  }
18037
- const modelRequirements = await this.getAgentModelRequirements();
18652
+ const modelRequirements = await this.getModelRequirements();
18038
18653
  const chatPrompt = prompt;
18039
18654
  let underlyingLlmResult;
18655
+ // Create modified chat prompt with agent system message
18656
+ const promptWithAgentModelRequirements = {
18657
+ ...chatPrompt,
18658
+ modelRequirements: {
18659
+ ...chatPrompt.modelRequirements,
18660
+ ...modelRequirements,
18661
+ // Spread tools to convert readonly array to mutable
18662
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : chatPrompt.modelRequirements.tools,
18663
+ // Prepend agent system message to existing system message
18664
+ systemMessage: modelRequirements.systemMessage +
18665
+ (chatPrompt.modelRequirements.systemMessage
18666
+ ? `\n\n${chatPrompt.modelRequirements.systemMessage}`
18667
+ : ''),
18668
+ },
18669
+ };
18670
+ console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
18040
18671
  if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
18041
18672
  const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
18042
18673
  const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
@@ -18057,6 +18688,7 @@ class AgentLlmExecutionTools {
18057
18688
  name: this.title,
18058
18689
  instructions: modelRequirements.systemMessage,
18059
18690
  knowledgeSources: modelRequirements.knowledgeSources,
18691
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
18060
18692
  });
18061
18693
  AgentLlmExecutionTools.assistantCache.set(this.title, {
18062
18694
  assistantId: assistant.assistantId,
@@ -18073,6 +18705,7 @@ class AgentLlmExecutionTools {
18073
18705
  name: this.title,
18074
18706
  instructions: modelRequirements.systemMessage,
18075
18707
  knowledgeSources: modelRequirements.knowledgeSources,
18708
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
18076
18709
  /*
18077
18710
  !!!
18078
18711
  metadata: {
@@ -18085,32 +18718,28 @@ class AgentLlmExecutionTools {
18085
18718
  requirementsHash,
18086
18719
  });
18087
18720
  }
18088
- underlyingLlmResult = await assistant.callChatModelStream(chatPrompt, onProgress);
18721
+ // Create modified chat prompt with agent system message specific to OpenAI Assistant
18722
+ const promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools = {
18723
+ ...promptWithAgentModelRequirements,
18724
+ modelRequirements: {
18725
+ ...promptWithAgentModelRequirements.modelRequirements,
18726
+ modelName: undefined,
18727
+ systemMessage: undefined,
18728
+ temperature: undefined, // <- Note: Let the Assistant use its default temperature
18729
+ },
18730
+ };
18731
+ console.log('!!!! promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools:', promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools);
18732
+ underlyingLlmResult = await assistant.callChatModelStream(promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools, onProgress);
18089
18733
  }
18090
18734
  else {
18091
18735
  if (this.options.isVerbose) {
18092
18736
  console.log(`2️⃣ Creating Assistant ${this.title} on generic LLM execution tools...`);
18093
18737
  }
18094
- // Create modified chat prompt with agent system message
18095
- const modifiedChatPrompt = {
18096
- ...chatPrompt,
18097
- modelRequirements: {
18098
- ...chatPrompt.modelRequirements,
18099
- ...modelRequirements,
18100
- // Spread tools to convert readonly array to mutable
18101
- tools: modelRequirements.tools ? [...modelRequirements.tools] : chatPrompt.modelRequirements.tools,
18102
- // Prepend agent system message to existing system message
18103
- systemMessage: modelRequirements.systemMessage +
18104
- (chatPrompt.modelRequirements.systemMessage
18105
- ? `\n\n${chatPrompt.modelRequirements.systemMessage}`
18106
- : ''),
18107
- },
18108
- };
18109
18738
  if (this.options.llmTools.callChatModelStream) {
18110
- underlyingLlmResult = await this.options.llmTools.callChatModelStream(modifiedChatPrompt, onProgress);
18739
+ underlyingLlmResult = await this.options.llmTools.callChatModelStream(promptWithAgentModelRequirements, onProgress);
18111
18740
  }
18112
18741
  else if (this.options.llmTools.callChatModel) {
18113
- underlyingLlmResult = await this.options.llmTools.callChatModel(modifiedChatPrompt);
18742
+ underlyingLlmResult = await this.options.llmTools.callChatModel(promptWithAgentModelRequirements);
18114
18743
  onProgress(underlyingLlmResult);
18115
18744
  }
18116
18745
  else {
@@ -18139,7 +18768,8 @@ AgentLlmExecutionTools.assistantCache = new Map();
18139
18768
  * TODO: [🧠] Adding parameter substitution support (here or should be responsibility of the underlying LLM Tools)
18140
18769
  */
18141
18770
 
18142
- var _Agent_instances, _Agent_selfLearn;
18771
+ var _Agent_instances, _Agent_selfLearnSamples;
18772
+ // !!!!! import { RemoteAgent } from './RemoteAgent'; // <- [💞] <- !!!!!
18143
18773
  /**
18144
18774
  * Represents one AI Agent
18145
18775
  *
@@ -18199,6 +18829,10 @@ class Agent extends AgentLlmExecutionTools {
18199
18829
  * This is parsed from commitments like USE BROWSER, USE SEARCH ENGINE, KNOWLEDGE, etc.
18200
18830
  */
18201
18831
  this.capabilities = [];
18832
+ /**
18833
+ * List of sample conversations (question/answer pairs)
18834
+ */
18835
+ this.samples = [];
18202
18836
  /**
18203
18837
  * Metadata like image or color
18204
18838
  */
@@ -18208,12 +18842,13 @@ class Agent extends AgentLlmExecutionTools {
18208
18842
  this.agentSource = agentSource;
18209
18843
  this.agentSource.subscribe((source) => {
18210
18844
  this.updateAgentSource(source);
18211
- const { agentName, personaDescription, initialMessage, links, meta, capabilities } = parseAgentSource(source);
18845
+ const { agentName, personaDescription, initialMessage, links, meta, capabilities, samples } = parseAgentSource(source);
18212
18846
  this._agentName = agentName;
18213
18847
  this.personaDescription = personaDescription;
18214
18848
  this.initialMessage = initialMessage;
18215
18849
  this.links = links;
18216
18850
  this.capabilities = capabilities;
18851
+ this.samples = samples;
18217
18852
  this.meta = { ...this.meta, ...meta };
18218
18853
  });
18219
18854
  }
@@ -18225,10 +18860,10 @@ class Agent extends AgentLlmExecutionTools {
18225
18860
  async callChatModelStream(prompt, onProgress) {
18226
18861
  var _a;
18227
18862
  // [1] Check if the user is asking the same thing as in the samples
18228
- const modelRequirements = await this.getAgentModelRequirements();
18863
+ const modelRequirements = await this.getModelRequirements();
18229
18864
  if (modelRequirements.samples) {
18230
18865
  const normalizedPrompt = normalizeMessageText(prompt.content);
18231
- const sample = modelRequirements.samples.find((sample) => normalizeMessageText(sample.question) === normalizedPrompt);
18866
+ const sample = modelRequirements.samples.find((sample) => sample.question !== null && normalizeMessageText(sample.question) === normalizedPrompt);
18232
18867
  if (sample) {
18233
18868
  const now = new Date().toISOString();
18234
18869
  const result = {
@@ -18274,17 +18909,22 @@ class Agent extends AgentLlmExecutionTools {
18274
18909
  if ((_a = modelRequirements.metadata) === null || _a === void 0 ? void 0 : _a.isClosed) {
18275
18910
  return result;
18276
18911
  }
18277
- await __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_selfLearn).call(this, prompt, result);
18278
- // <- TODO: !!!! Do not await self-learn, run in background with error handling
18912
+ // TODO: !!!!! Is this timed properly?
18913
+ // Note: [1] Do the append of the samples
18914
+ __classPrivateFieldGet(this, _Agent_instances, "m", _Agent_selfLearnSamples).call(this, prompt, result);
18915
+ /*
18916
+ !!!!!
18917
+ // Note: [2] Asynchronously call the teacher agent and invoke the silver link. When the teacher fails, keep just the samples
18918
+ this.#selfLearnTeacher(prompt, result).catch((error) => {
18919
+ if (this.options.isVerbose) {
18920
+ console.error('Failed to self-learn from teacher agent', error);
18921
+ }
18922
+ });
18923
+ */
18279
18924
  return result;
18280
18925
  }
18281
18926
  }
18282
- _Agent_instances = new WeakSet(), _Agent_selfLearn =
18283
- /**
18284
- * Self-learning: Appends the conversation and extracted knowledge to the agent source
18285
- */
18286
- async function _Agent_selfLearn(prompt, result) {
18287
- // Learning: Append the conversation sample to the agent source
18927
+ _Agent_instances = new WeakSet(), _Agent_selfLearnSamples = function _Agent_selfLearnSamples(prompt, result) {
18288
18928
  const learningExample = spaceTrim$2((block) => `
18289
18929
 
18290
18930
  ---
@@ -18296,52 +18936,9 @@ async function _Agent_selfLearn(prompt, result) {
18296
18936
  ${block(result.content)}
18297
18937
 
18298
18938
  `);
18299
- // Extract knowledge
18300
- let knowledgeBlock = '';
18301
- try {
18302
- const extractionPrompt = {
18303
- title: 'Knowledge Extraction',
18304
- modelRequirements: {
18305
- modelVariant: 'CHAT',
18306
- },
18307
- content: spaceTrim$2((block) => `
18308
- You are an AI agent that is learning from a conversation.
18309
-
18310
- Here is the conversation so far:
18311
-
18312
- User: ${block(prompt.content)}
18313
- Agent: ${block(result.content)}
18314
-
18315
- Extract any new knowledge, facts, or important information that should be remembered for future interactions.
18316
- Format the output as a list of KNOWLEDGE blocks.
18317
- If there is no new knowledge, return nothing.
18318
-
18319
- Example output:
18320
- KNOWLEDGE The user's name is Alice.
18321
- KNOWLEDGE The project deadline is next Friday.
18322
- `),
18323
- pipelineUrl: 'https://github.com/webgptorg/promptbook/blob/main/prompts/knowledge-extraction.ptbk.md',
18324
- parameters: {},
18325
- };
18326
- if (this.options.llmTools.callChatModel) {
18327
- const extractionResult = await this.options.llmTools.callChatModel(extractionPrompt);
18328
- const extractedContent = extractionResult.content;
18329
- if (extractedContent.includes('KNOWLEDGE')) {
18330
- knowledgeBlock = '\n\n' + spaceTrim$2(extractedContent);
18331
- }
18332
- }
18333
- else {
18334
- // TODO: [🧠] Fallback to callChatModelStream if callChatModel is not available
18335
- }
18336
- }
18337
- catch (error) {
18338
- if (this.options.isVerbose) {
18339
- console.warn('Failed to extract knowledge', error);
18340
- }
18341
- }
18342
18939
  // Append to the current source
18343
18940
  const currentSource = this.agentSource.value;
18344
- const newSource = padBook(validateBook(spaceTrim$2(currentSource) + '\n\n' + learningExample + knowledgeBlock));
18941
+ const newSource = padBook(validateBook(spaceTrim$2(currentSource) + '\n\n' + learningExample));
18345
18942
  // Update the source (which will trigger the subscription and update the underlying tools)
18346
18943
  this.agentSource.next(newSource);
18347
18944
  };