@promptbook/remote-server 0.105.0-21 â 0.105.0-23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.es.js +1050 -68
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/browser.index.d.ts +2 -0
- package/esm/typings/src/_packages/core.index.d.ts +9 -13
- package/esm/typings/src/_packages/node.index.d.ts +2 -0
- package/esm/typings/src/_packages/types.index.d.ts +12 -2
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +1 -1
- package/esm/typings/src/book-components/Chat/AgentChip/AgentChip.d.ts +67 -0
- package/esm/typings/src/book-components/Chat/AgentChip/index.d.ts +2 -0
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +15 -0
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +10 -0
- package/esm/typings/src/book-components/Chat/LlmChat/FriendlyErrorMessage.d.ts +20 -0
- package/esm/typings/src/book-components/Chat/LlmChat/LlmChatProps.d.ts +8 -0
- package/esm/typings/src/book-components/Chat/SourceChip/SourceChip.d.ts +35 -0
- package/esm/typings/src/book-components/Chat/SourceChip/index.d.ts +2 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +21 -0
- package/esm/typings/src/book-components/Chat/utils/getToolCallChipletText.d.ts +21 -0
- package/esm/typings/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +53 -0
- package/esm/typings/src/commitments/TEMPLATE/TEMPLATE.d.ts +44 -0
- package/esm/typings/src/commitments/TEMPLATE/TEMPLATE.test.d.ts +1 -0
- package/esm/typings/src/commitments/USE_BROWSER/USE_BROWSER.d.ts +16 -2
- package/esm/typings/src/commitments/USE_BROWSER/fetchUrlContent.d.ts +22 -0
- package/esm/typings/src/commitments/USE_BROWSER/fetchUrlContentViaBrowser.d.ts +13 -0
- package/esm/typings/src/commitments/USE_EMAIL/USE_EMAIL.d.ts +47 -0
- package/esm/typings/src/commitments/_common/getAllCommitmentDefinitions.d.ts +8 -0
- package/esm/typings/src/commitments/_common/getAllCommitmentTypes.d.ts +8 -0
- package/esm/typings/src/commitments/_common/getAllCommitmentsToolFunctionsForBrowser.d.ts +10 -0
- package/esm/typings/src/commitments/_common/getAllCommitmentsToolFunctionsForNode.d.ts +14 -0
- package/esm/typings/src/commitments/_common/getAllCommitmentsToolTitles.d.ts +7 -0
- package/esm/typings/src/commitments/_common/getCommitmentDefinition.d.ts +10 -0
- package/esm/typings/src/commitments/_common/getGroupedCommitmentDefinitions.d.ts +17 -0
- package/esm/typings/src/commitments/_common/isCommitmentSupported.d.ts +9 -0
- package/esm/typings/src/commitments/index.d.ts +3 -64
- package/esm/typings/src/executables/$provideExecutablesForNode.d.ts +1 -0
- package/esm/typings/src/execution/utils/$provideExecutionToolsForNode.d.ts +1 -0
- package/esm/typings/src/scrapers/_common/register/$provideFilesystemForNode.d.ts +1 -0
- package/esm/typings/src/scrapers/_common/register/$provideScrapersForNode.d.ts +1 -0
- package/esm/typings/src/scrapers/_common/register/$provideScriptingForNode.d.ts +1 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/esm/typings/src/wizard/wizard.d.ts +1 -4
- package/package.json +5 -2
- package/umd/index.umd.js +1050 -71
- package/umd/index.umd.js.map +1 -1
package/esm/index.es.js
CHANGED
|
@@ -9,13 +9,16 @@ import http from 'http';
|
|
|
9
9
|
import { Server } from 'socket.io';
|
|
10
10
|
import swaggerUi from 'swagger-ui-express';
|
|
11
11
|
import { randomBytes } from 'crypto';
|
|
12
|
-
import { stat, access, constants, readFile, writeFile, readdir, mkdir, watch } from 'fs/promises';
|
|
12
|
+
import { stat, access, constants, readFile, writeFile, readdir, mkdir, watch, rm } from 'fs/promises';
|
|
13
13
|
import { Subject } from 'rxjs';
|
|
14
14
|
import hexEncoder from 'crypto-js/enc-hex';
|
|
15
15
|
import sha256 from 'crypto-js/sha256';
|
|
16
16
|
import { SHA256 } from 'crypto-js';
|
|
17
17
|
import { lookup, extension } from 'mime-types';
|
|
18
18
|
import { parse, unparse } from 'papaparse';
|
|
19
|
+
import { Readability } from '@mozilla/readability';
|
|
20
|
+
import { JSDOM } from 'jsdom';
|
|
21
|
+
import { Converter } from 'showdown';
|
|
19
22
|
import moment from 'moment';
|
|
20
23
|
import { createElement } from 'react';
|
|
21
24
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
@@ -34,7 +37,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
34
37
|
* @generated
|
|
35
38
|
* @see https://github.com/webgptorg/promptbook
|
|
36
39
|
*/
|
|
37
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.105.0-
|
|
40
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.105.0-23';
|
|
38
41
|
/**
|
|
39
42
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
40
43
|
* Note: [đ] Ignore a discrepancy between file name and entity name
|
|
@@ -1918,6 +1921,7 @@ function $provideFilesystemForNode(options) {
|
|
|
1918
1921
|
}
|
|
1919
1922
|
/**
|
|
1920
1923
|
* Note: [đĸ] Code in this file should never be never released in packages that could be imported into browser environment
|
|
1924
|
+
* TODO: [đ] Unite `xxxForServer` and `xxxForNode` naming
|
|
1921
1925
|
*/
|
|
1922
1926
|
|
|
1923
1927
|
/**
|
|
@@ -2104,6 +2108,7 @@ async function $provideExecutablesForNode(options) {
|
|
|
2104
2108
|
/**
|
|
2105
2109
|
* TODO: [đ§ ] Allow to override the executables without need to call `locatePandoc` / `locateLibreoffice` in case of provided
|
|
2106
2110
|
* Note: [đĸ] Code in this file should never be never released in packages that could be imported into browser environment
|
|
2111
|
+
* TODO: [đ] Unite `xxxForServer` and `xxxForNode` naming
|
|
2107
2112
|
*/
|
|
2108
2113
|
|
|
2109
2114
|
/**
|
|
@@ -7977,6 +7982,7 @@ async function $provideScrapersForNode(tools, options) {
|
|
|
7977
7982
|
}
|
|
7978
7983
|
/**
|
|
7979
7984
|
* Note: [đĸ] Code in this file should never be never released in packages that could be imported into browser environment
|
|
7985
|
+
* TODO: [đ] Unite `xxxForServer` and `xxxForNode` naming
|
|
7980
7986
|
*/
|
|
7981
7987
|
|
|
7982
7988
|
/**
|
|
@@ -8275,6 +8281,536 @@ function isValidAgentUrl(url) {
|
|
|
8275
8281
|
* TODO: [đ ] Maybe more info why the URL is invalid
|
|
8276
8282
|
*/
|
|
8277
8283
|
|
|
8284
|
+
/**
|
|
8285
|
+
* Retrieves an intermediate source for a scraper based on the knowledge source.
|
|
8286
|
+
* Manages the caching and retrieval of intermediate scraper results for optimized performance.
|
|
8287
|
+
*
|
|
8288
|
+
* @private as internal utility for scrapers
|
|
8289
|
+
*/
|
|
8290
|
+
async function getScraperIntermediateSource(source, options) {
|
|
8291
|
+
const { filename: sourceFilename, url } = source;
|
|
8292
|
+
const { rootDirname, cacheDirname, intermediateFilesStrategy, extension, isVerbose } = options;
|
|
8293
|
+
// TODO: [đŦ] DRY
|
|
8294
|
+
const hash = SHA256(
|
|
8295
|
+
// <- TODO: [đĨŦ] Encapsulate sha256 to some private utility function
|
|
8296
|
+
hexEncoder.parse(sourceFilename || url || 'untitled'))
|
|
8297
|
+
.toString( /* hex */)
|
|
8298
|
+
.substring(0, 20);
|
|
8299
|
+
// <- TODO: [đĨŦ] Make some system for hashes and ids of promptbook
|
|
8300
|
+
const semanticName = normalizeToKebabCase(titleToName((sourceFilename || url || '').split('intermediate').join(''))).substring(0, 20);
|
|
8301
|
+
// <- TODO: [đąâđ]
|
|
8302
|
+
const pieces = ['intermediate', semanticName, hash].filter((piece) => piece !== '');
|
|
8303
|
+
const name = pieces.join('-').split('--').join('-');
|
|
8304
|
+
const cacheFilename = join(process.cwd(), cacheDirname, ...nameToSubfolderPath(hash /* <- TODO: [đ] Maybe add some SHA256 prefix */), name)
|
|
8305
|
+
.split('\\')
|
|
8306
|
+
.join('/') +
|
|
8307
|
+
'.' +
|
|
8308
|
+
extension;
|
|
8309
|
+
// Note: Try to create cache directory, but don't fail if filesystem has issues
|
|
8310
|
+
try {
|
|
8311
|
+
await mkdir(dirname(cacheFilename), { recursive: true });
|
|
8312
|
+
}
|
|
8313
|
+
catch (error) {
|
|
8314
|
+
// Note: If we can't create cache directory, continue without it
|
|
8315
|
+
// This handles read-only filesystems, permission issues, and missing parent directories
|
|
8316
|
+
if (error instanceof Error &&
|
|
8317
|
+
(error.message.includes('EROFS') ||
|
|
8318
|
+
error.message.includes('read-only') ||
|
|
8319
|
+
error.message.includes('EACCES') ||
|
|
8320
|
+
error.message.includes('EPERM') ||
|
|
8321
|
+
error.message.includes('ENOENT'))) ;
|
|
8322
|
+
else {
|
|
8323
|
+
// Re-throw other unexpected errors
|
|
8324
|
+
throw error;
|
|
8325
|
+
}
|
|
8326
|
+
}
|
|
8327
|
+
let isDestroyed = true;
|
|
8328
|
+
const fileHandler = {
|
|
8329
|
+
filename: cacheFilename,
|
|
8330
|
+
get isDestroyed() {
|
|
8331
|
+
return isDestroyed;
|
|
8332
|
+
},
|
|
8333
|
+
async destroy() {
|
|
8334
|
+
if (intermediateFilesStrategy === 'HIDE_AND_CLEAN') {
|
|
8335
|
+
if (isVerbose) {
|
|
8336
|
+
console.info('legacyDocumentScraper: Clening cache');
|
|
8337
|
+
}
|
|
8338
|
+
await rm(cacheFilename);
|
|
8339
|
+
// TODO: [đŋ][đ§ ] Maybe remove empty folders
|
|
8340
|
+
}
|
|
8341
|
+
isDestroyed = true;
|
|
8342
|
+
},
|
|
8343
|
+
};
|
|
8344
|
+
return fileHandler;
|
|
8345
|
+
}
|
|
8346
|
+
/**
|
|
8347
|
+
* Note: Not using `FileCacheStorage` for two reasons:
|
|
8348
|
+
* 1) Need to store more than serialized JSONs
|
|
8349
|
+
* 2) Need to switch between a `rootDirname` and `cacheDirname` <- TODO: [đĄ]
|
|
8350
|
+
* TODO: [đąâđ][đ§ ] Make some smart crop
|
|
8351
|
+
* Note: [đĸ] Code in this file should never be never released in packages that could be imported into browser environment
|
|
8352
|
+
*/
|
|
8353
|
+
|
|
8354
|
+
/**
|
|
8355
|
+
* Metadata of the scraper
|
|
8356
|
+
*
|
|
8357
|
+
* @private within the scraper directory
|
|
8358
|
+
*/
|
|
8359
|
+
const markdownScraperMetadata = $deepFreeze({
|
|
8360
|
+
title: 'Markdown scraper',
|
|
8361
|
+
packageName: '@promptbook/markdown-utils',
|
|
8362
|
+
className: 'MarkdownScraper',
|
|
8363
|
+
mimeTypes: ['text/markdown', 'text/plain'],
|
|
8364
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
|
|
8365
|
+
isAvailableInBrowser: true,
|
|
8366
|
+
// <- Note: [đ] This is the only scraper which makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
|
|
8367
|
+
requiredExecutables: [],
|
|
8368
|
+
}); /* <- Note: [đ¤] */
|
|
8369
|
+
/**
|
|
8370
|
+
* Registration of known scraper metadata
|
|
8371
|
+
*
|
|
8372
|
+
* Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
|
|
8373
|
+
*
|
|
8374
|
+
* @public exported from `@promptbook/core`
|
|
8375
|
+
* @public exported from `@promptbook/wizard`
|
|
8376
|
+
* @public exported from `@promptbook/cli`
|
|
8377
|
+
*/
|
|
8378
|
+
$scrapersMetadataRegister.register(markdownScraperMetadata);
|
|
8379
|
+
/**
|
|
8380
|
+
* Note: [đ] Ignore a discrepancy between file name and entity name
|
|
8381
|
+
*/
|
|
8382
|
+
|
|
8383
|
+
/**
|
|
8384
|
+
* Scraper for markdown files
|
|
8385
|
+
*
|
|
8386
|
+
* @see `documentationUrl` for more details
|
|
8387
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
8388
|
+
*/
|
|
8389
|
+
class MarkdownScraper {
|
|
8390
|
+
/**
|
|
8391
|
+
* Metadata of the scraper which includes title, mime types, etc.
|
|
8392
|
+
*/
|
|
8393
|
+
get metadata() {
|
|
8394
|
+
return markdownScraperMetadata;
|
|
8395
|
+
}
|
|
8396
|
+
constructor(tools, options) {
|
|
8397
|
+
this.tools = tools;
|
|
8398
|
+
this.options = options;
|
|
8399
|
+
}
|
|
8400
|
+
/**
|
|
8401
|
+
* Scrapes the markdown file and returns the knowledge pieces or `null` if it can't scrape it
|
|
8402
|
+
*/
|
|
8403
|
+
async scrape(source) {
|
|
8404
|
+
const { maxParallelCount = DEFAULT_MAX_PARALLEL_COUNT, isVerbose = DEFAULT_IS_VERBOSE } = this.options;
|
|
8405
|
+
const { llm } = this.tools;
|
|
8406
|
+
if (llm === undefined) {
|
|
8407
|
+
throw new MissingToolsError('LLM tools are required for scraping external files');
|
|
8408
|
+
// <- Note: This scraper is used in all other scrapers, so saying "external files" not "markdown files"
|
|
8409
|
+
}
|
|
8410
|
+
const llmTools = getSingleLlmExecutionTools(llm);
|
|
8411
|
+
// TODO: [đŧ] In future use `ptbk make` and made getPipelineCollection
|
|
8412
|
+
const collection = createPipelineCollectionFromJson(...PipelineCollection);
|
|
8413
|
+
const prepareKnowledgeFromMarkdownExecutor = createPipelineExecutor({
|
|
8414
|
+
pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book'),
|
|
8415
|
+
tools: {
|
|
8416
|
+
llm: llm,
|
|
8417
|
+
},
|
|
8418
|
+
});
|
|
8419
|
+
const prepareTitleExecutor = createPipelineExecutor({
|
|
8420
|
+
pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-title.book'),
|
|
8421
|
+
tools: {
|
|
8422
|
+
llm: llm,
|
|
8423
|
+
},
|
|
8424
|
+
});
|
|
8425
|
+
const prepareKeywordsExecutor = createPipelineExecutor({
|
|
8426
|
+
pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-keywords.book'),
|
|
8427
|
+
tools: {
|
|
8428
|
+
llm: llm,
|
|
8429
|
+
},
|
|
8430
|
+
});
|
|
8431
|
+
const knowledgeContent = await source.asText();
|
|
8432
|
+
const result = await prepareKnowledgeFromMarkdownExecutor({ knowledgeContent }).asPromise({
|
|
8433
|
+
isCrashedOnError: true,
|
|
8434
|
+
});
|
|
8435
|
+
const { outputParameters } = result;
|
|
8436
|
+
const { knowledgePieces: knowledgePiecesRaw } = outputParameters;
|
|
8437
|
+
const knowledgeTextPieces = (knowledgePiecesRaw || '').split('\n---\n');
|
|
8438
|
+
// <- TODO: [main] Smarter split and filter out empty pieces
|
|
8439
|
+
if (isVerbose) {
|
|
8440
|
+
console.info('knowledgeTextPieces:', knowledgeTextPieces);
|
|
8441
|
+
}
|
|
8442
|
+
// const usage = ;
|
|
8443
|
+
const knowledge = await Promise.all(
|
|
8444
|
+
// TODO: [đĒ] Do not send all at once but in chunks
|
|
8445
|
+
knowledgeTextPieces.map(async (knowledgeTextPiece, i) => {
|
|
8446
|
+
// Note: These are just default values, they will be overwritten by the actual values:
|
|
8447
|
+
let name = `piece-${i}`;
|
|
8448
|
+
let title = spaceTrim$2(knowledgeTextPiece.substring(0, 100));
|
|
8449
|
+
const knowledgePieceContent = spaceTrim$2(knowledgeTextPiece);
|
|
8450
|
+
let keywords = [];
|
|
8451
|
+
const index = [];
|
|
8452
|
+
/*
|
|
8453
|
+
TODO: [â] Track line and column of the source
|
|
8454
|
+
const sources: KnowledgePiecePreparedJson['sources'] = [
|
|
8455
|
+
];
|
|
8456
|
+
*/
|
|
8457
|
+
try {
|
|
8458
|
+
const titleResult = await prepareTitleExecutor({ knowledgePieceContent }).asPromise({
|
|
8459
|
+
isCrashedOnError: true,
|
|
8460
|
+
});
|
|
8461
|
+
const { title: titleRaw = 'Untitled' } = titleResult.outputParameters;
|
|
8462
|
+
title = spaceTrim$2(titleRaw) /* <- TODO: Maybe do in pipeline */;
|
|
8463
|
+
name = titleToName(title);
|
|
8464
|
+
// --- Keywords
|
|
8465
|
+
const keywordsResult = await prepareKeywordsExecutor({ knowledgePieceContent }).asPromise({
|
|
8466
|
+
isCrashedOnError: true,
|
|
8467
|
+
});
|
|
8468
|
+
const { keywords: keywordsRaw = '' } = keywordsResult.outputParameters;
|
|
8469
|
+
keywords = (keywordsRaw || '')
|
|
8470
|
+
.split(',')
|
|
8471
|
+
.map((keyword) => keyword.trim())
|
|
8472
|
+
.filter((keyword) => keyword !== '');
|
|
8473
|
+
if (isVerbose) {
|
|
8474
|
+
console.info(`Keywords for "${title}":`, keywords);
|
|
8475
|
+
}
|
|
8476
|
+
// ---
|
|
8477
|
+
if (!llmTools.callEmbeddingModel) {
|
|
8478
|
+
// TODO: [đĨ] Detect browser / node and make it colorful
|
|
8479
|
+
console.error('No callEmbeddingModel function provided');
|
|
8480
|
+
}
|
|
8481
|
+
else {
|
|
8482
|
+
// TODO: [đ§ ][đ] Embedding via multiple models
|
|
8483
|
+
const embeddingResult = await llmTools.callEmbeddingModel({
|
|
8484
|
+
title: `Embedding for ${title}` /* <- Note: No impact on embedding result itself, just for logging */,
|
|
8485
|
+
parameters: {},
|
|
8486
|
+
content: knowledgePieceContent,
|
|
8487
|
+
modelRequirements: {
|
|
8488
|
+
modelVariant: 'EMBEDDING',
|
|
8489
|
+
},
|
|
8490
|
+
});
|
|
8491
|
+
index.push({
|
|
8492
|
+
modelName: embeddingResult.modelName,
|
|
8493
|
+
position: [...embeddingResult.content],
|
|
8494
|
+
// <- TODO: [đĒ] Here should be no need for spreading new array, just `position: embeddingResult.content`
|
|
8495
|
+
});
|
|
8496
|
+
}
|
|
8497
|
+
}
|
|
8498
|
+
catch (error) {
|
|
8499
|
+
// Note: Here is expected error:
|
|
8500
|
+
// > PipelineExecutionError: You have not provided any `LlmExecutionTools` that support model variant "EMBEDDING
|
|
8501
|
+
if (!(error instanceof PipelineExecutionError)) {
|
|
8502
|
+
throw error;
|
|
8503
|
+
}
|
|
8504
|
+
// TODO: [đĨ] Detect browser / node and make it colorful
|
|
8505
|
+
console.error(error, "<- Note: This error is not critical to prepare the pipeline, just knowledge pieces won't have embeddings");
|
|
8506
|
+
}
|
|
8507
|
+
return {
|
|
8508
|
+
name,
|
|
8509
|
+
title,
|
|
8510
|
+
content: knowledgePieceContent,
|
|
8511
|
+
keywords,
|
|
8512
|
+
index,
|
|
8513
|
+
// <- TODO: [â] sources,
|
|
8514
|
+
};
|
|
8515
|
+
}));
|
|
8516
|
+
return knowledge;
|
|
8517
|
+
}
|
|
8518
|
+
}
|
|
8519
|
+
/**
|
|
8520
|
+
* TODO: [đĒ] Do it in parallel 11:11
|
|
8521
|
+
* Note: No need to aggregate usage here, it is done by intercepting the llmTools
|
|
8522
|
+
*/
|
|
8523
|
+
|
|
8524
|
+
/**
|
|
8525
|
+
* Metadata of the scraper
|
|
8526
|
+
*
|
|
8527
|
+
* @private within the scraper directory
|
|
8528
|
+
*/
|
|
8529
|
+
const websiteScraperMetadata = $deepFreeze({
|
|
8530
|
+
title: 'Website scraper',
|
|
8531
|
+
packageName: '@promptbook/website-crawler',
|
|
8532
|
+
className: 'WebsiteScraper',
|
|
8533
|
+
mimeTypes: ['text/html'],
|
|
8534
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
|
|
8535
|
+
isAvailableInBrowser: false,
|
|
8536
|
+
// <- Note: [đ] Only `MarkdownScraper` makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
|
|
8537
|
+
requiredExecutables: [],
|
|
8538
|
+
}); /* <- Note: [đ¤] */
|
|
8539
|
+
/**
|
|
8540
|
+
* Registration of known scraper metadata
|
|
8541
|
+
*
|
|
8542
|
+
* Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
|
|
8543
|
+
*
|
|
8544
|
+
* @public exported from `@promptbook/core`
|
|
8545
|
+
* @public exported from `@promptbook/wizard`
|
|
8546
|
+
* @public exported from `@promptbook/cli`
|
|
8547
|
+
*/
|
|
8548
|
+
$scrapersMetadataRegister.register(websiteScraperMetadata);
|
|
8549
|
+
/**
|
|
8550
|
+
* Note: [đ] Ignore a discrepancy between file name and entity name
|
|
8551
|
+
*/
|
|
8552
|
+
|
|
8553
|
+
/**
|
|
8554
|
+
* Create a new showdown converter instance
|
|
8555
|
+
*
|
|
8556
|
+
* @private utility of `WebsiteScraper`
|
|
8557
|
+
*/
|
|
8558
|
+
function createShowdownConverter() {
|
|
8559
|
+
return new Converter({
|
|
8560
|
+
flavor: 'github',
|
|
8561
|
+
/*
|
|
8562
|
+
> import showdownHighlight from 'showdown-highlight';
|
|
8563
|
+
> extensions: [
|
|
8564
|
+
> showdownHighlight({
|
|
8565
|
+
> // Whether to add the classes to the <pre> tag, default is false
|
|
8566
|
+
> pre: true,
|
|
8567
|
+
> // Whether to use hljs' auto language detection, default is true
|
|
8568
|
+
> auto_detection: true,
|
|
8569
|
+
> }),
|
|
8570
|
+
> ],
|
|
8571
|
+
*/
|
|
8572
|
+
});
|
|
8573
|
+
}
|
|
8574
|
+
|
|
8575
|
+
// TODO: [đŗâđ] Finally take pick of .json vs .ts
|
|
8576
|
+
/**
|
|
8577
|
+
* Scraper for websites
|
|
8578
|
+
*
|
|
8579
|
+
* @see `documentationUrl` for more details
|
|
8580
|
+
* @public exported from `@promptbook/website-crawler`
|
|
8581
|
+
*/
|
|
8582
|
+
class WebsiteScraper {
|
|
8583
|
+
/**
|
|
8584
|
+
* Metadata of the scraper which includes title, mime types, etc.
|
|
8585
|
+
*/
|
|
8586
|
+
get metadata() {
|
|
8587
|
+
return websiteScraperMetadata;
|
|
8588
|
+
}
|
|
8589
|
+
constructor(tools, options) {
|
|
8590
|
+
this.tools = tools;
|
|
8591
|
+
this.options = options;
|
|
8592
|
+
this.markdownScraper = new MarkdownScraper(tools, options);
|
|
8593
|
+
this.showdownConverter = createShowdownConverter();
|
|
8594
|
+
}
|
|
8595
|
+
/**
|
|
8596
|
+
* Convert the website to `.md` file and returns intermediate source
|
|
8597
|
+
*
|
|
8598
|
+
* Note: `$` is used to indicate that this function is not a pure function - it leaves files on the disk and you are responsible for cleaning them by calling `destroy` method of returned object
|
|
8599
|
+
*/
|
|
8600
|
+
async $convert(source) {
|
|
8601
|
+
const {
|
|
8602
|
+
// TODO: [đ§ ] Maybe in node use headless browser not just JSDOM
|
|
8603
|
+
rootDirname = process.cwd(), cacheDirname = DEFAULT_SCRAPE_CACHE_DIRNAME, intermediateFilesStrategy = DEFAULT_INTERMEDIATE_FILES_STRATEGY, isVerbose = DEFAULT_IS_VERBOSE, } = this.options;
|
|
8604
|
+
if (source.url === null) {
|
|
8605
|
+
throw new KnowledgeScrapeError('Website scraper requires URL');
|
|
8606
|
+
}
|
|
8607
|
+
if (this.tools.fs === undefined) {
|
|
8608
|
+
throw new EnvironmentMismatchError('Can not scrape websites without filesystem tools');
|
|
8609
|
+
}
|
|
8610
|
+
const jsdom = new JSDOM(await source.asText(), {
|
|
8611
|
+
url: source.url,
|
|
8612
|
+
});
|
|
8613
|
+
const reader = new Readability(jsdom.window.document);
|
|
8614
|
+
const article = reader.parse();
|
|
8615
|
+
// console.log(article);
|
|
8616
|
+
// await forTime(10000);
|
|
8617
|
+
let html = (article === null || article === void 0 ? void 0 : article.content) || (article === null || article === void 0 ? void 0 : article.textContent) || jsdom.window.document.body.innerHTML;
|
|
8618
|
+
// Note: Unwrap html such as it is convertable by `markdownConverter`
|
|
8619
|
+
for (let i = 0; i < 2; i++) {
|
|
8620
|
+
html = html.replace(/<div\s*(?:id="readability-page-\d+"\s+class="page")?>(.*)<\/div>/is, '$1');
|
|
8621
|
+
}
|
|
8622
|
+
if (html.includes('<div')) {
|
|
8623
|
+
html = (article === null || article === void 0 ? void 0 : article.textContent) || '';
|
|
8624
|
+
}
|
|
8625
|
+
const cacheFilehandler = await getScraperIntermediateSource(source, {
|
|
8626
|
+
rootDirname,
|
|
8627
|
+
cacheDirname,
|
|
8628
|
+
intermediateFilesStrategy,
|
|
8629
|
+
extension: 'html',
|
|
8630
|
+
isVerbose,
|
|
8631
|
+
});
|
|
8632
|
+
// Note: Try to cache the scraped content, but don't fail if the filesystem is read-only
|
|
8633
|
+
try {
|
|
8634
|
+
await this.tools.fs.writeFile(cacheFilehandler.filename, html, 'utf-8');
|
|
8635
|
+
}
|
|
8636
|
+
catch (error) {
|
|
8637
|
+
// Note: If we can't write to cache, we'll continue without caching
|
|
8638
|
+
// This handles read-only filesystems like Vercel
|
|
8639
|
+
if (error instanceof Error &&
|
|
8640
|
+
(error.message.includes('EROFS') ||
|
|
8641
|
+
error.message.includes('read-only') ||
|
|
8642
|
+
error.message.includes('EACCES') ||
|
|
8643
|
+
error.message.includes('EPERM') ||
|
|
8644
|
+
error.message.includes('ENOENT'))) ;
|
|
8645
|
+
else {
|
|
8646
|
+
// Re-throw other unexpected errors
|
|
8647
|
+
throw error;
|
|
8648
|
+
}
|
|
8649
|
+
}
|
|
8650
|
+
const markdown = this.showdownConverter.makeMarkdown(html, jsdom.window.document);
|
|
8651
|
+
return { ...cacheFilehandler, markdown };
|
|
8652
|
+
}
|
|
8653
|
+
/**
|
|
8654
|
+
* Scrapes the website and returns the knowledge pieces or `null` if it can't scrape it
|
|
8655
|
+
*/
|
|
8656
|
+
async scrape(source) {
|
|
8657
|
+
const cacheFilehandler = await this.$convert(source);
|
|
8658
|
+
const markdownSource = {
|
|
8659
|
+
source: source.source,
|
|
8660
|
+
filename: cacheFilehandler.filename,
|
|
8661
|
+
url: null,
|
|
8662
|
+
mimeType: 'text/markdown',
|
|
8663
|
+
asText() {
|
|
8664
|
+
return cacheFilehandler.markdown;
|
|
8665
|
+
},
|
|
8666
|
+
asJson() {
|
|
8667
|
+
throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
|
|
8668
|
+
},
|
|
8669
|
+
/*
|
|
8670
|
+
TODO: [đĨŊ]
|
|
8671
|
+
> asBlob() {
|
|
8672
|
+
> throw new UnexpectedError(
|
|
8673
|
+
> 'Did not expect that `markdownScraper` would need to get the content `asBlob`',
|
|
8674
|
+
> );
|
|
8675
|
+
> },
|
|
8676
|
+
*/
|
|
8677
|
+
};
|
|
8678
|
+
const knowledge = this.markdownScraper.scrape(markdownSource);
|
|
8679
|
+
await cacheFilehandler.destroy();
|
|
8680
|
+
return knowledge;
|
|
8681
|
+
}
|
|
8682
|
+
}
|
|
8683
|
+
/**
|
|
8684
|
+
* TODO: [đŖ] Scraped website in .md can act as cache item - there is no need to run conversion each time
|
|
8685
|
+
* TODO: [đĒ] Do it in parallel 11:11
|
|
8686
|
+
* Note: No need to aggregate usage here, it is done by intercepting the llmTools
|
|
8687
|
+
* Note: [đĸ] Code in this file should never be never released in packages that could be imported into browser environment
|
|
8688
|
+
*/
|
|
8689
|
+
|
|
8690
|
+
/**
|
|
8691
|
+
* Fetches and scrapes content from a URL (SERVER-SIDE ONLY)
|
|
8692
|
+
*
|
|
8693
|
+
* This function:
|
|
8694
|
+
* 1. Fetches the URL content using promptbookFetch
|
|
8695
|
+
* 2. Determines the content type (HTML, PDF, etc.)
|
|
8696
|
+
* 3. Uses the appropriate scraper to convert to markdown
|
|
8697
|
+
* 4. Returns the scraped markdown content
|
|
8698
|
+
*
|
|
8699
|
+
* @param url The URL to fetch and scrape
|
|
8700
|
+
* @returns Markdown content from the URL
|
|
8701
|
+
*
|
|
8702
|
+
* @private internal utility for USE BROWSER commitment
|
|
8703
|
+
*
|
|
8704
|
+
* WARNING: This function should NOT be used directly in browser environments.
|
|
8705
|
+
* For browser environments, use fetchUrlContentViaBrowser which proxies through
|
|
8706
|
+
* the Agents Server API endpoint at /api/scrape
|
|
8707
|
+
*/
|
|
8708
|
+
async function fetchUrlContent(url) {
|
|
8709
|
+
try {
|
|
8710
|
+
// Validate URL
|
|
8711
|
+
let parsedUrl;
|
|
8712
|
+
try {
|
|
8713
|
+
parsedUrl = new URL(url);
|
|
8714
|
+
}
|
|
8715
|
+
catch (error) {
|
|
8716
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
8717
|
+
}
|
|
8718
|
+
// Fetch the URL content
|
|
8719
|
+
const response = await promptbookFetch(url);
|
|
8720
|
+
if (!response.ok) {
|
|
8721
|
+
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
|
|
8722
|
+
}
|
|
8723
|
+
// Get content type
|
|
8724
|
+
const contentType = response.headers.get('content-type') || 'text/html';
|
|
8725
|
+
const content = await response.text();
|
|
8726
|
+
// Determine the appropriate scraper based on content type
|
|
8727
|
+
const mimeType = (contentType.split(';')[0] || 'text/html').trim();
|
|
8728
|
+
// Handle PDFs
|
|
8729
|
+
if (mimeType === 'application/pdf' || url.toLowerCase().endsWith('.pdf')) {
|
|
8730
|
+
// TODO: [đ°] Implement PDF scraping using PdfScraper
|
|
8731
|
+
// For now, return a message indicating PDF support is coming
|
|
8732
|
+
return spaceTrim$1(`
|
|
8733
|
+
# PDF Document: ${parsedUrl.pathname.split('/').pop()}
|
|
8734
|
+
|
|
8735
|
+
**Note:** PDF scraping is not yet fully implemented.
|
|
8736
|
+
|
|
8737
|
+
**URL:** ${url}
|
|
8738
|
+
|
|
8739
|
+
The PDF scraper will be integrated to provide full document content extraction.
|
|
8740
|
+
For now, please try to find an HTML version of this content or use a different source.
|
|
8741
|
+
`);
|
|
8742
|
+
}
|
|
8743
|
+
// Handle HTML/websites
|
|
8744
|
+
if (mimeType === 'text/html' || mimeType === 'application/xhtml+xml' || !mimeType.includes('/')) {
|
|
8745
|
+
// Create a scraper source handler
|
|
8746
|
+
const sourceHandler = {
|
|
8747
|
+
source: url,
|
|
8748
|
+
filename: null,
|
|
8749
|
+
url,
|
|
8750
|
+
mimeType: 'text/html',
|
|
8751
|
+
asText: () => content,
|
|
8752
|
+
asJson: () => {
|
|
8753
|
+
throw new Error('JSON conversion not supported for HTML content');
|
|
8754
|
+
},
|
|
8755
|
+
};
|
|
8756
|
+
// Use WebsiteScraper to convert HTML to markdown
|
|
8757
|
+
// Note: We need minimal tools for the scraper
|
|
8758
|
+
const scraper = new WebsiteScraper(// <- TODO: !!!!! Things marked with [đĸ] should never be never released in packages that could be imported into browser environment
|
|
8759
|
+
{
|
|
8760
|
+
// Minimal tools - fs and llm are optional for basic scraping
|
|
8761
|
+
fs: undefined,
|
|
8762
|
+
llm: undefined,
|
|
8763
|
+
}, {
|
|
8764
|
+
isVerbose: false,
|
|
8765
|
+
});
|
|
8766
|
+
// Convert to markdown
|
|
8767
|
+
const intermediateSource = await scraper.$convert(sourceHandler);
|
|
8768
|
+
const markdown = intermediateSource.markdown;
|
|
8769
|
+
// Clean up intermediate files
|
|
8770
|
+
await intermediateSource.destroy();
|
|
8771
|
+
// Add URL header to the content
|
|
8772
|
+
return spaceTrim$1(`
|
|
8773
|
+
# Content from: ${url}
|
|
8774
|
+
|
|
8775
|
+
${markdown}
|
|
8776
|
+
|
|
8777
|
+
---
|
|
8778
|
+
|
|
8779
|
+
*Source: ${url}*
|
|
8780
|
+
`);
|
|
8781
|
+
}
|
|
8782
|
+
// For other content types, return the raw content with a note
|
|
8783
|
+
return spaceTrim$1(`
|
|
8784
|
+
# Content from: ${url}
|
|
8785
|
+
|
|
8786
|
+
**Content Type:** ${contentType}
|
|
8787
|
+
|
|
8788
|
+
${content}
|
|
8789
|
+
|
|
8790
|
+
---
|
|
8791
|
+
|
|
8792
|
+
*Source: ${url}*
|
|
8793
|
+
`);
|
|
8794
|
+
}
|
|
8795
|
+
catch (error) {
|
|
8796
|
+
// Handle errors gracefully
|
|
8797
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
8798
|
+
return spaceTrim$1(`
|
|
8799
|
+
# Error fetching content from URL
|
|
8800
|
+
|
|
8801
|
+
**URL:** ${url}
|
|
8802
|
+
|
|
8803
|
+
**Error:** ${errorMessage}
|
|
8804
|
+
|
|
8805
|
+
Unable to fetch and scrape the content from this URL.
|
|
8806
|
+
Please verify the URL is correct and accessible.
|
|
8807
|
+
`);
|
|
8808
|
+
}
|
|
8809
|
+
}
|
|
8810
|
+
/**
|
|
8811
|
+
* Note: [đĸ] Code in this file should never be never released in packages that could be imported into browser environment
|
|
8812
|
+
*/
|
|
8813
|
+
|
|
8278
8814
|
/**
|
|
8279
8815
|
* Generates a regex pattern to match a specific commitment
|
|
8280
8816
|
*
|
|
@@ -11650,6 +12186,129 @@ function stripToolCallLines(text) {
|
|
|
11650
12186
|
* Note: [đ] Ignore a discrepancy between file name and entity name
|
|
11651
12187
|
*/
|
|
11652
12188
|
|
|
12189
|
+
/**
|
|
12190
|
+
* TEMPLATE commitment definition
|
|
12191
|
+
*
|
|
12192
|
+
* The TEMPLATE commitment enforces a specific response structure or template
|
|
12193
|
+
* that the agent must follow when generating responses. This helps ensure
|
|
12194
|
+
* consistent message formatting across all agent interactions.
|
|
12195
|
+
*
|
|
12196
|
+
* Example usage in agent source:
|
|
12197
|
+
*
|
|
12198
|
+
* ```book
|
|
12199
|
+
* TEMPLATE Always structure your response with: 1) Summary, 2) Details, 3) Next steps
|
|
12200
|
+
* TEMPLATE Use the following format: **Question:** [user question] | **Answer:** [your answer]
|
|
12201
|
+
* ```
|
|
12202
|
+
*
|
|
12203
|
+
* When used without content, it enables template mode which instructs the agent
|
|
12204
|
+
* to follow any template patterns defined in other commitments or context.
|
|
12205
|
+
*
|
|
12206
|
+
* @private [đĒ] Maybe export the commitments through some package
|
|
12207
|
+
*/
|
|
12208
|
+
class TemplateCommitmentDefinition extends BaseCommitmentDefinition {
|
|
12209
|
+
constructor(type = 'TEMPLATE') {
|
|
12210
|
+
super(type);
|
|
12211
|
+
}
|
|
12212
|
+
/**
|
|
12213
|
+
* Short one-line description of TEMPLATE.
|
|
12214
|
+
*/
|
|
12215
|
+
get description() {
|
|
12216
|
+
return 'Enforce a specific message structure or response template.';
|
|
12217
|
+
}
|
|
12218
|
+
/**
|
|
12219
|
+
* Icon for this commitment.
|
|
12220
|
+
*/
|
|
12221
|
+
get icon() {
|
|
12222
|
+
return 'đ';
|
|
12223
|
+
}
|
|
12224
|
+
/**
|
|
12225
|
+
* Markdown documentation for TEMPLATE commitment.
|
|
12226
|
+
*/
|
|
12227
|
+
get documentation() {
|
|
12228
|
+
return spaceTrim$1(`
|
|
12229
|
+
# ${this.type}
|
|
12230
|
+
|
|
12231
|
+
Enforces a specific response structure or template that the agent must follow when generating responses.
|
|
12232
|
+
|
|
12233
|
+
## Key aspects
|
|
12234
|
+
|
|
12235
|
+
- Both terms work identically and can be used interchangeably.
|
|
12236
|
+
- Can be used with or without content.
|
|
12237
|
+
- When used without content, enables template mode for structured responses.
|
|
12238
|
+
- When used with content, defines the specific template structure to follow.
|
|
12239
|
+
- Multiple templates can be combined, with later ones taking precedence.
|
|
12240
|
+
|
|
12241
|
+
## Examples
|
|
12242
|
+
|
|
12243
|
+
\`\`\`book
|
|
12244
|
+
Customer Support Agent
|
|
12245
|
+
|
|
12246
|
+
PERSONA You are a helpful customer support representative
|
|
12247
|
+
TEMPLATE Always structure your response with: 1) Acknowledgment, 2) Solution, 3) Follow-up question
|
|
12248
|
+
STYLE Be professional and empathetic
|
|
12249
|
+
\`\`\`
|
|
12250
|
+
|
|
12251
|
+
\`\`\`book
|
|
12252
|
+
Technical Documentation Assistant
|
|
12253
|
+
|
|
12254
|
+
PERSONA You are a technical writing expert
|
|
12255
|
+
TEMPLATE Use the following format: **Topic:** [topic] | **Explanation:** [details] | **Example:** [code]
|
|
12256
|
+
FORMAT Use markdown with clear headings
|
|
12257
|
+
\`\`\`
|
|
12258
|
+
|
|
12259
|
+
\`\`\`book
|
|
12260
|
+
Simple Agent
|
|
12261
|
+
|
|
12262
|
+
PERSONA You are a virtual assistant
|
|
12263
|
+
TEMPLATE
|
|
12264
|
+
\`\`\`
|
|
12265
|
+
`);
|
|
12266
|
+
}
|
|
12267
|
+
/**
|
|
12268
|
+
* TEMPLATE can be used with or without content.
|
|
12269
|
+
*/
|
|
12270
|
+
get requiresContent() {
|
|
12271
|
+
return false;
|
|
12272
|
+
}
|
|
12273
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
12274
|
+
var _a;
|
|
12275
|
+
const trimmedContent = content.trim();
|
|
12276
|
+
// If no content is provided, enable template mode
|
|
12277
|
+
if (!trimmedContent) {
|
|
12278
|
+
// Store template mode flag in metadata
|
|
12279
|
+
const updatedMetadata = {
|
|
12280
|
+
...requirements.metadata,
|
|
12281
|
+
templateMode: true,
|
|
12282
|
+
};
|
|
12283
|
+
// Add a general instruction about using structured templates
|
|
12284
|
+
const templateModeInstruction = spaceTrim$1(`
|
|
12285
|
+
Use a clear, structured template format for your responses.
|
|
12286
|
+
Maintain consistency in how you organize and present information.
|
|
12287
|
+
`);
|
|
12288
|
+
return {
|
|
12289
|
+
...this.appendToSystemMessage(requirements, templateModeInstruction, '\n\n'),
|
|
12290
|
+
metadata: updatedMetadata,
|
|
12291
|
+
};
|
|
12292
|
+
}
|
|
12293
|
+
// If content is provided, add the specific template instructions
|
|
12294
|
+
const templateSection = `Response Template: ${trimmedContent}`;
|
|
12295
|
+
// Store the template in metadata for potential programmatic access
|
|
12296
|
+
const existingTemplates = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.templates) || [];
|
|
12297
|
+
const updatedMetadata = {
|
|
12298
|
+
...requirements.metadata,
|
|
12299
|
+
templates: [...existingTemplates, trimmedContent],
|
|
12300
|
+
templateMode: true,
|
|
12301
|
+
};
|
|
12302
|
+
return {
|
|
12303
|
+
...this.appendToSystemMessage(requirements, templateSection, '\n\n'),
|
|
12304
|
+
metadata: updatedMetadata,
|
|
12305
|
+
};
|
|
12306
|
+
}
|
|
12307
|
+
}
|
|
12308
|
+
/**
|
|
12309
|
+
* Note: [đ] Ignore a discrepancy between file name and entity name
|
|
12310
|
+
*/
|
|
12311
|
+
|
|
11653
12312
|
/**
|
|
11654
12313
|
* USE commitment definition
|
|
11655
12314
|
*
|
|
@@ -11764,12 +12423,56 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11764
12423
|
* Note: [đ] Ignore a discrepancy between file name and entity name
|
|
11765
12424
|
*/
|
|
11766
12425
|
|
|
12426
|
+
/**
|
|
12427
|
+
* Client-side safe wrapper for fetching URL content
|
|
12428
|
+
*
|
|
12429
|
+
* This function proxies requests to the Agents Server API endpoint for scraping,
|
|
12430
|
+
* making it safe to use in browser environments.
|
|
12431
|
+
*
|
|
12432
|
+
* @param url The URL to fetch and scrape
|
|
12433
|
+
* @param agentsServerUrl The base URL of the agents server (defaults to current origin)
|
|
12434
|
+
* @returns Markdown content from the URL
|
|
12435
|
+
*
|
|
12436
|
+
* @private internal utility for USE BROWSER commitment
|
|
12437
|
+
*/
|
|
12438
|
+
async function fetchUrlContentViaBrowser(url, agentsServerUrl) {
|
|
12439
|
+
try {
|
|
12440
|
+
// Determine the agents server URL
|
|
12441
|
+
const baseUrl = agentsServerUrl || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
12442
|
+
if (!baseUrl) {
|
|
12443
|
+
throw new Error('Agents server URL is required in non-browser environments');
|
|
12444
|
+
}
|
|
12445
|
+
// Build the API endpoint URL
|
|
12446
|
+
const apiUrl = new URL('/api/scrape', baseUrl);
|
|
12447
|
+
apiUrl.searchParams.set('url', url);
|
|
12448
|
+
// Fetch from the API endpoint
|
|
12449
|
+
const response = await fetch(apiUrl.toString());
|
|
12450
|
+
if (!response.ok) {
|
|
12451
|
+
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
12452
|
+
throw new Error(`Failed to scrape URL: ${errorData.error || response.statusText}`);
|
|
12453
|
+
}
|
|
12454
|
+
const data = await response.json();
|
|
12455
|
+
if (!data.success || !data.content) {
|
|
12456
|
+
throw new Error(`Scraping failed: ${data.error || 'No content returned'}`);
|
|
12457
|
+
}
|
|
12458
|
+
return data.content;
|
|
12459
|
+
}
|
|
12460
|
+
catch (error) {
|
|
12461
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
12462
|
+
throw new Error(`Error fetching URL content via browser: ${errorMessage}`);
|
|
12463
|
+
}
|
|
12464
|
+
}
|
|
12465
|
+
|
|
11767
12466
|
/**
|
|
11768
12467
|
* USE BROWSER commitment definition
|
|
11769
12468
|
*
|
|
11770
|
-
* The `USE BROWSER` commitment indicates that the agent should utilize
|
|
12469
|
+
* The `USE BROWSER` commitment indicates that the agent should utilize browser tools
|
|
11771
12470
|
* to access and retrieve up-to-date information from the internet when necessary.
|
|
11772
12471
|
*
|
|
12472
|
+
* This commitment provides two levels of browser access:
|
|
12473
|
+
* 1. One-shot URL fetching: Simple function to fetch and scrape URL content
|
|
12474
|
+
* 2. Running browser: For complex tasks like scrolling, clicking, etc. (prepared but not active yet)
|
|
12475
|
+
*
|
|
11773
12476
|
* The content following `USE BROWSER` is ignored (similar to NOTE).
|
|
11774
12477
|
*
|
|
11775
12478
|
* Example usage in agent source:
|
|
@@ -11795,7 +12498,7 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11795
12498
|
* Short one-line description of USE BROWSER.
|
|
11796
12499
|
*/
|
|
11797
12500
|
get description() {
|
|
11798
|
-
return 'Enable the agent to use
|
|
12501
|
+
return 'Enable the agent to use browser tools for accessing internet information.';
|
|
11799
12502
|
}
|
|
11800
12503
|
/**
|
|
11801
12504
|
* Icon for this commitment.
|
|
@@ -11810,14 +12513,18 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11810
12513
|
return spaceTrim$1(`
|
|
11811
12514
|
# USE BROWSER
|
|
11812
12515
|
|
|
11813
|
-
Enables the agent to use
|
|
12516
|
+
Enables the agent to use browser tools to access and retrieve up-to-date information from the internet.
|
|
11814
12517
|
|
|
11815
12518
|
## Key aspects
|
|
11816
12519
|
|
|
11817
12520
|
- The content following \`USE BROWSER\` is ignored (similar to NOTE)
|
|
12521
|
+
- Provides two levels of browser access:
|
|
12522
|
+
1. **One-shot URL fetching**: Simple function to fetch and scrape URL content (active)
|
|
12523
|
+
2. **Running browser**: For complex tasks like scrolling, clicking, etc. (prepared but not active yet)
|
|
11818
12524
|
- The actual browser tool usage is handled by the agent runtime
|
|
11819
|
-
- Allows the agent to fetch current information from websites
|
|
12525
|
+
- Allows the agent to fetch current information from websites and documents
|
|
11820
12526
|
- Useful for research tasks, fact-checking, and accessing dynamic content
|
|
12527
|
+
- Supports various content types including HTML pages and PDF documents
|
|
11821
12528
|
|
|
11822
12529
|
## Examples
|
|
11823
12530
|
|
|
@@ -11853,48 +12560,306 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
11853
12560
|
*/
|
|
11854
12561
|
getToolTitles() {
|
|
11855
12562
|
return {
|
|
11856
|
-
|
|
12563
|
+
fetch_url_content: 'Fetch URL content',
|
|
12564
|
+
run_browser: 'Run browser',
|
|
11857
12565
|
};
|
|
11858
12566
|
}
|
|
11859
12567
|
applyToAgentModelRequirements(requirements, content) {
|
|
11860
12568
|
// Get existing tools array or create new one
|
|
11861
12569
|
const existingTools = requirements.tools || [];
|
|
11862
|
-
// Add
|
|
11863
|
-
const
|
|
12570
|
+
// Add browser tools if not already present
|
|
12571
|
+
const toolsToAdd = [];
|
|
12572
|
+
// Tool 1: One-shot URL content fetching
|
|
12573
|
+
if (!existingTools.some((tool) => tool.name === 'fetch_url_content')) {
|
|
12574
|
+
toolsToAdd.push({
|
|
12575
|
+
name: 'fetch_url_content',
|
|
12576
|
+
description: spaceTrim$1(`
|
|
12577
|
+
Fetches and scrapes the content from a URL (webpage or document).
|
|
12578
|
+
This tool retrieves the content of the specified URL and converts it to markdown format.
|
|
12579
|
+
Use this when you need to access information from a specific website or document.
|
|
12580
|
+
Supports various content types including HTML pages and PDF documents.
|
|
12581
|
+
`),
|
|
12582
|
+
parameters: {
|
|
12583
|
+
type: 'object',
|
|
12584
|
+
properties: {
|
|
12585
|
+
url: {
|
|
12586
|
+
type: 'string',
|
|
12587
|
+
description: 'The URL to fetch and scrape (e.g., "https://example.com" or "https://example.com/document.pdf")',
|
|
12588
|
+
},
|
|
12589
|
+
},
|
|
12590
|
+
required: ['url'],
|
|
12591
|
+
},
|
|
12592
|
+
});
|
|
12593
|
+
}
|
|
12594
|
+
// Tool 2: Running browser (prepared but not active yet)
|
|
12595
|
+
if (!existingTools.some((tool) => tool.name === 'run_browser')) {
|
|
12596
|
+
toolsToAdd.push({
|
|
12597
|
+
name: 'run_browser',
|
|
12598
|
+
description: spaceTrim$1(`
|
|
12599
|
+
Launches a browser session for complex interactions.
|
|
12600
|
+
This tool is for advanced browser automation tasks like scrolling, clicking, form filling, etc.
|
|
12601
|
+
Note: This tool is prepared but not yet active. It will be implemented in a future update.
|
|
12602
|
+
`),
|
|
12603
|
+
parameters: {
|
|
12604
|
+
type: 'object',
|
|
12605
|
+
properties: {
|
|
12606
|
+
url: {
|
|
12607
|
+
type: 'string',
|
|
12608
|
+
description: 'The initial URL to navigate to',
|
|
12609
|
+
},
|
|
12610
|
+
actions: {
|
|
12611
|
+
type: 'array',
|
|
12612
|
+
description: 'Array of actions to perform in the browser',
|
|
12613
|
+
items: {
|
|
12614
|
+
type: 'object',
|
|
12615
|
+
properties: {
|
|
12616
|
+
type: {
|
|
12617
|
+
type: 'string',
|
|
12618
|
+
enum: ['navigate', 'click', 'scroll', 'type', 'wait'],
|
|
12619
|
+
},
|
|
12620
|
+
selector: {
|
|
12621
|
+
type: 'string',
|
|
12622
|
+
description: 'CSS selector for the element (for click, type actions)',
|
|
12623
|
+
},
|
|
12624
|
+
value: {
|
|
12625
|
+
type: 'string',
|
|
12626
|
+
description: 'Value to type or navigate to',
|
|
12627
|
+
},
|
|
12628
|
+
},
|
|
12629
|
+
},
|
|
12630
|
+
},
|
|
12631
|
+
},
|
|
12632
|
+
required: ['url'],
|
|
12633
|
+
},
|
|
12634
|
+
});
|
|
12635
|
+
}
|
|
12636
|
+
const updatedTools = [...existingTools, ...toolsToAdd];
|
|
12637
|
+
// Return requirements with updated tools and metadata
|
|
12638
|
+
return this.appendToSystemMessage({
|
|
12639
|
+
...requirements,
|
|
12640
|
+
tools: updatedTools,
|
|
12641
|
+
metadata: {
|
|
12642
|
+
...requirements.metadata,
|
|
12643
|
+
useBrowser: true,
|
|
12644
|
+
},
|
|
12645
|
+
}, spaceTrim$1(`
|
|
12646
|
+
You have access to browser tools to fetch and access content from the internet.
|
|
12647
|
+
- Use "fetch_url_content" to retrieve content from specific URLs (webpages or documents)
|
|
12648
|
+
- Use "run_browser" for complex browser interactions (note: not yet active)
|
|
12649
|
+
When you need to know information from a specific website or document, use the fetch_url_content tool.
|
|
12650
|
+
`));
|
|
12651
|
+
}
|
|
12652
|
+
/**
|
|
12653
|
+
* Gets the browser tool function implementations.
|
|
12654
|
+
*
|
|
12655
|
+
* This method automatically detects the environment and uses:
|
|
12656
|
+
* - Server-side: Direct scraping via fetchUrlContent (Node.js)
|
|
12657
|
+
* - Browser: Proxy through Agents Server API via fetchUrlContentViaBrowser
|
|
12658
|
+
*/
|
|
12659
|
+
getToolFunctions() {
|
|
12660
|
+
return {
|
|
12661
|
+
/**
|
|
12662
|
+
* @@@
|
|
12663
|
+
*
|
|
12664
|
+
* Note: [đē] This function has implementation both for browser and node, this is the proxied one for browser
|
|
12665
|
+
*/
|
|
12666
|
+
async fetch_url_content(args) {
|
|
12667
|
+
console.log('!!!! [Tool] fetch_url_content called', { args });
|
|
12668
|
+
const { url } = args;
|
|
12669
|
+
return await fetchUrlContentViaBrowser(url);
|
|
12670
|
+
},
|
|
12671
|
+
/**
|
|
12672
|
+
* @@@
|
|
12673
|
+
*/
|
|
12674
|
+
async run_browser(args) {
|
|
12675
|
+
console.log('!!!! [Tool] run_browser called', { args });
|
|
12676
|
+
const { url } = args;
|
|
12677
|
+
// This tool is prepared but not active yet
|
|
12678
|
+
return spaceTrim$1(`
|
|
12679
|
+
# Running browser
|
|
12680
|
+
|
|
12681
|
+
The running browser tool is not yet active.
|
|
12682
|
+
|
|
12683
|
+
Requested URL: ${url}
|
|
12684
|
+
|
|
12685
|
+
This feature will be implemented in a future update to support:
|
|
12686
|
+
- Complex browser interactions
|
|
12687
|
+
- Scrolling and navigation
|
|
12688
|
+
- Clicking and form filling
|
|
12689
|
+
- Taking screenshots
|
|
12690
|
+
|
|
12691
|
+
For now, please use the "fetch_url_content" tool instead.
|
|
12692
|
+
`);
|
|
12693
|
+
},
|
|
12694
|
+
};
|
|
12695
|
+
}
|
|
12696
|
+
}
|
|
12697
|
+
/**
|
|
12698
|
+
* Note: [đ] Ignore a discrepancy between file name and entity name
|
|
12699
|
+
*/
|
|
12700
|
+
|
|
12701
|
+
/**
|
|
12702
|
+
* @@@
|
|
12703
|
+
*
|
|
12704
|
+
* @private utility for commitments
|
|
12705
|
+
*/
|
|
12706
|
+
function formatOptionalInstructionBlock(label, content) {
|
|
12707
|
+
const trimmedContent = spaceTrim$1(content);
|
|
12708
|
+
if (!trimmedContent) {
|
|
12709
|
+
return '';
|
|
12710
|
+
}
|
|
12711
|
+
return spaceTrim$1((block) => `
|
|
12712
|
+
- ${label}:
|
|
12713
|
+
${block(trimmedContent
|
|
12714
|
+
.split('\n')
|
|
12715
|
+
.map((line) => `- ${line}`)
|
|
12716
|
+
.join('\n'))}
|
|
12717
|
+
`);
|
|
12718
|
+
}
|
|
12719
|
+
|
|
12720
|
+
/**
|
|
12721
|
+
* USE EMAIL commitment definition
|
|
12722
|
+
*
|
|
12723
|
+
* The `USE EMAIL` commitment enables the agent to send emails.
|
|
12724
|
+
*
|
|
12725
|
+
* Example usage in agent source:
|
|
12726
|
+
*
|
|
12727
|
+
* ```book
|
|
12728
|
+
* USE EMAIL
|
|
12729
|
+
* USE EMAIL Write always formal and polite emails, always greet.
|
|
12730
|
+
* ```
|
|
12731
|
+
*
|
|
12732
|
+
* @private [đĒ] Maybe export the commitments through some package
|
|
12733
|
+
*/
|
|
12734
|
+
class UseEmailCommitmentDefinition extends BaseCommitmentDefinition {
|
|
12735
|
+
constructor() {
|
|
12736
|
+
super('USE EMAIL', ['EMAIL', 'MAIL']);
|
|
12737
|
+
}
|
|
12738
|
+
get requiresContent() {
|
|
12739
|
+
return false;
|
|
12740
|
+
}
|
|
12741
|
+
/**
|
|
12742
|
+
* Short one-line description of USE EMAIL.
|
|
12743
|
+
*/
|
|
12744
|
+
get description() {
|
|
12745
|
+
return 'Enable the agent to send emails.';
|
|
12746
|
+
}
|
|
12747
|
+
/**
|
|
12748
|
+
* Icon for this commitment.
|
|
12749
|
+
*/
|
|
12750
|
+
get icon() {
|
|
12751
|
+
return 'đ§';
|
|
12752
|
+
}
|
|
12753
|
+
/**
|
|
12754
|
+
* Markdown documentation for USE EMAIL commitment.
|
|
12755
|
+
*/
|
|
12756
|
+
get documentation() {
|
|
12757
|
+
return spaceTrim$1(`
|
|
12758
|
+
# USE EMAIL
|
|
12759
|
+
|
|
12760
|
+
Enables the agent to send emails through the email service.
|
|
12761
|
+
|
|
12762
|
+
## Key aspects
|
|
12763
|
+
|
|
12764
|
+
- The agent can send emails to specified recipients.
|
|
12765
|
+
- Supports multiple recipients, CC, subject, and markdown content.
|
|
12766
|
+
- Emails are queued and sent through configured email providers.
|
|
12767
|
+
- The content following \`USE EMAIL\` can provide additional instructions for email composition (e.g., style, tone, formatting preferences).
|
|
12768
|
+
|
|
12769
|
+
## Examples
|
|
12770
|
+
|
|
12771
|
+
\`\`\`book
|
|
12772
|
+
Email Assistant
|
|
12773
|
+
|
|
12774
|
+
PERSONA You are a helpful assistant who can send emails.
|
|
12775
|
+
USE EMAIL
|
|
12776
|
+
\`\`\`
|
|
12777
|
+
|
|
12778
|
+
\`\`\`book
|
|
12779
|
+
Formal Email Assistant
|
|
12780
|
+
|
|
12781
|
+
PERSONA You help with professional communication.
|
|
12782
|
+
USE EMAIL Write always formal and polite emails, always greet.
|
|
12783
|
+
\`\`\`
|
|
12784
|
+
`);
|
|
12785
|
+
}
|
|
12786
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
12787
|
+
const extraInstructions = formatOptionalInstructionBlock('Email instructions', content);
|
|
12788
|
+
// Get existing tools array or create new one
|
|
12789
|
+
const existingTools = requirements.tools || [];
|
|
12790
|
+
// Add 'send_email' to tools if not already present
|
|
12791
|
+
const updatedTools = existingTools.some((tool) => tool.name === 'send_email')
|
|
11864
12792
|
? existingTools
|
|
11865
|
-
:
|
|
11866
|
-
// TODO: [đ°] Use through proper MCP server
|
|
12793
|
+
: [
|
|
11867
12794
|
...existingTools,
|
|
11868
12795
|
{
|
|
11869
|
-
name: '
|
|
11870
|
-
description:
|
|
11871
|
-
A tool that can browse the web.
|
|
11872
|
-
Use this tool when you need to access specific websites or browse the internet.
|
|
11873
|
-
`),
|
|
12796
|
+
name: 'send_email',
|
|
12797
|
+
description: `Send an email to one or more recipients. ${!content ? '' : `Style instructions: ${content}`}`,
|
|
11874
12798
|
parameters: {
|
|
11875
12799
|
type: 'object',
|
|
11876
12800
|
properties: {
|
|
11877
|
-
|
|
12801
|
+
to: {
|
|
12802
|
+
type: 'array',
|
|
12803
|
+
items: { type: 'string' },
|
|
12804
|
+
description: 'Array of recipient email addresses (e.g., ["user@example.com", "Jane Doe <jane@example.com>"])',
|
|
12805
|
+
},
|
|
12806
|
+
cc: {
|
|
12807
|
+
type: 'array',
|
|
12808
|
+
items: { type: 'string' },
|
|
12809
|
+
description: 'Optional array of CC email addresses',
|
|
12810
|
+
},
|
|
12811
|
+
subject: {
|
|
12812
|
+
type: 'string',
|
|
12813
|
+
description: 'Email subject line',
|
|
12814
|
+
},
|
|
12815
|
+
body: {
|
|
11878
12816
|
type: 'string',
|
|
11879
|
-
description: '
|
|
12817
|
+
description: 'Email body content in markdown format',
|
|
11880
12818
|
},
|
|
11881
12819
|
},
|
|
11882
|
-
required: ['
|
|
12820
|
+
required: ['to', 'subject', 'body'],
|
|
11883
12821
|
},
|
|
11884
12822
|
},
|
|
11885
|
-
|
|
12823
|
+
// <- TODO: !!!! define the function in LLM tools
|
|
12824
|
+
];
|
|
11886
12825
|
// Return requirements with updated tools and metadata
|
|
11887
12826
|
return this.appendToSystemMessage({
|
|
11888
12827
|
...requirements,
|
|
11889
12828
|
tools: updatedTools,
|
|
11890
12829
|
metadata: {
|
|
11891
12830
|
...requirements.metadata,
|
|
11892
|
-
|
|
12831
|
+
useEmail: content || true,
|
|
11893
12832
|
},
|
|
11894
|
-
}, spaceTrim$1(`
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
12833
|
+
}, spaceTrim$1((block) => `
|
|
12834
|
+
Email tool:
|
|
12835
|
+
- You have access to send emails via the tool "send_email".
|
|
12836
|
+
- Use it when you need to send emails to users or other recipients.
|
|
12837
|
+
- The email body should be written in markdown format.
|
|
12838
|
+
- Always ensure the email content is clear, professional, and appropriate.
|
|
12839
|
+
${block(extraInstructions)}
|
|
12840
|
+
`));
|
|
12841
|
+
}
|
|
12842
|
+
/**
|
|
12843
|
+
* Gets human-readable titles for tool functions provided by this commitment.
|
|
12844
|
+
*/
|
|
12845
|
+
getToolTitles() {
|
|
12846
|
+
return {
|
|
12847
|
+
send_email: 'Send email',
|
|
12848
|
+
};
|
|
12849
|
+
}
|
|
12850
|
+
/**
|
|
12851
|
+
* Gets the `send_email` tool function implementation.
|
|
12852
|
+
* Note: This is a placeholder - the actual implementation is provided by the agent server.
|
|
12853
|
+
*/
|
|
12854
|
+
getToolFunctions() {
|
|
12855
|
+
return {
|
|
12856
|
+
async send_email(args) {
|
|
12857
|
+
console.log('!!!! [Tool] send_email called', { args });
|
|
12858
|
+
// This is a placeholder implementation
|
|
12859
|
+
// The actual implementation should be provided by the agent server
|
|
12860
|
+
throw new Error('send_email tool not implemented. This commitment requires integration with an email service.');
|
|
12861
|
+
},
|
|
12862
|
+
};
|
|
11898
12863
|
}
|
|
11899
12864
|
}
|
|
11900
12865
|
/**
|
|
@@ -12163,25 +13128,6 @@ class SerpSearchEngine {
|
|
|
12163
13128
|
}
|
|
12164
13129
|
}
|
|
12165
13130
|
|
|
12166
|
-
/**
|
|
12167
|
-
* @@@
|
|
12168
|
-
*
|
|
12169
|
-
* @private utility for commitments
|
|
12170
|
-
*/
|
|
12171
|
-
function formatOptionalInstructionBlock(label, content) {
|
|
12172
|
-
const trimmedContent = spaceTrim$1(content);
|
|
12173
|
-
if (!trimmedContent) {
|
|
12174
|
-
return '';
|
|
12175
|
-
}
|
|
12176
|
-
return spaceTrim$1((block) => `
|
|
12177
|
-
- ${label}:
|
|
12178
|
-
${block(trimmedContent
|
|
12179
|
-
.split('\n')
|
|
12180
|
-
.map((line) => `- ${line}`)
|
|
12181
|
-
.join('\n'))}
|
|
12182
|
-
`);
|
|
12183
|
-
}
|
|
12184
|
-
|
|
12185
13131
|
/**
|
|
12186
13132
|
* USE SEARCH ENGINE commitment definition
|
|
12187
13133
|
*
|
|
@@ -12610,6 +13556,8 @@ const COMMITMENT_REGISTRY = [
|
|
|
12610
13556
|
new SampleCommitmentDefinition('EXAMPLE'),
|
|
12611
13557
|
new FormatCommitmentDefinition('FORMAT'),
|
|
12612
13558
|
new FormatCommitmentDefinition('FORMATS'),
|
|
13559
|
+
new TemplateCommitmentDefinition('TEMPLATE'),
|
|
13560
|
+
new TemplateCommitmentDefinition('TEMPLATES'),
|
|
12613
13561
|
new FromCommitmentDefinition('FROM'),
|
|
12614
13562
|
new ImportCommitmentDefinition('IMPORT'),
|
|
12615
13563
|
new ImportCommitmentDefinition('IMPORTS'),
|
|
@@ -12648,15 +13596,13 @@ const COMMITMENT_REGISTRY = [
|
|
|
12648
13596
|
new UseBrowserCommitmentDefinition(),
|
|
12649
13597
|
new UseSearchEngineCommitmentDefinition(),
|
|
12650
13598
|
new UseTimeCommitmentDefinition(),
|
|
13599
|
+
new UseEmailCommitmentDefinition(),
|
|
12651
13600
|
new UseImageGeneratorCommitmentDefinition('USE IMAGE GENERATOR'),
|
|
12652
|
-
|
|
12653
|
-
new UseImageGeneratorCommitmentDefinition('
|
|
12654
|
-
|
|
12655
|
-
new UseImageGeneratorCommitmentDefinition('IMAGE
|
|
12656
|
-
//
|
|
12657
|
-
new UseImageGeneratorCommitmentDefinition('IMAGE GENERATION'),
|
|
12658
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12659
|
-
new UseImageGeneratorCommitmentDefinition('USE IMAGE'),
|
|
13601
|
+
new UseImageGeneratorCommitmentDefinition('USE IMAGE GENERATION' /* <- TODO: Remove any */),
|
|
13602
|
+
new UseImageGeneratorCommitmentDefinition('IMAGE GENERATOR' /* <- TODO: Remove any */),
|
|
13603
|
+
new UseImageGeneratorCommitmentDefinition('IMAGE GENERATION' /* <- TODO: Remove any */),
|
|
13604
|
+
new UseImageGeneratorCommitmentDefinition('USE IMAGE' /* <- TODO: Remove any */),
|
|
13605
|
+
// <- Note: [âšī¸] How to deal with commitment aliases with defined functions
|
|
12660
13606
|
new UseMcpCommitmentDefinition(),
|
|
12661
13607
|
new UseCommitmentDefinition(),
|
|
12662
13608
|
// Not yet implemented commitments (using placeholder)
|
|
@@ -12668,6 +13614,11 @@ const COMMITMENT_REGISTRY = [
|
|
|
12668
13614
|
new NotYetImplementedCommitmentDefinition('CONTEXT'),
|
|
12669
13615
|
// <- TODO: Prompt: Leverage aliases instead of duplicating commitment definitions
|
|
12670
13616
|
];
|
|
13617
|
+
/**
|
|
13618
|
+
* TODO: [đ§ ] Maybe create through standardized $register
|
|
13619
|
+
* Note: [đ] Ignore a discrepancy between file name and entity name
|
|
13620
|
+
*/
|
|
13621
|
+
|
|
12671
13622
|
/**
|
|
12672
13623
|
* Gets all available commitment definitions
|
|
12673
13624
|
* @returns Array of all commitment definitions
|
|
@@ -12677,24 +13628,65 @@ const COMMITMENT_REGISTRY = [
|
|
|
12677
13628
|
function getAllCommitmentDefinitions() {
|
|
12678
13629
|
return $deepFreeze([...COMMITMENT_REGISTRY]);
|
|
12679
13630
|
}
|
|
13631
|
+
|
|
12680
13632
|
/**
|
|
12681
13633
|
* Gets all function implementations provided by all commitments
|
|
12682
13634
|
*
|
|
12683
|
-
*
|
|
13635
|
+
* Note: This function is intended for browser use, there is also equivalent `getAllCommitmentsToolFunctionsForNode` for server use
|
|
13636
|
+
*
|
|
13637
|
+
* @public exported from `@promptbook/browser`
|
|
12684
13638
|
*/
|
|
12685
|
-
function
|
|
13639
|
+
function getAllCommitmentsToolFunctionsForBrowser() {
|
|
12686
13640
|
const allToolFunctions = {};
|
|
12687
13641
|
for (const commitmentDefinition of getAllCommitmentDefinitions()) {
|
|
12688
13642
|
const toolFunctions = commitmentDefinition.getToolFunctions();
|
|
12689
13643
|
for (const [funcName, funcImpl] of Object.entries(toolFunctions)) {
|
|
13644
|
+
if (allToolFunctions[funcName] !== undefined &&
|
|
13645
|
+
just(false) /* <- Note: [âšī¸] How to deal with commitment aliases */) {
|
|
13646
|
+
throw new UnexpectedError(`Duplicate tool function name detected: \`${funcName}\` provided by commitment \`${commitmentDefinition.type}\``);
|
|
13647
|
+
}
|
|
12690
13648
|
allToolFunctions[funcName] = funcImpl;
|
|
12691
13649
|
}
|
|
12692
13650
|
}
|
|
12693
13651
|
return allToolFunctions;
|
|
12694
13652
|
}
|
|
13653
|
+
|
|
12695
13654
|
/**
|
|
12696
|
-
*
|
|
12697
|
-
*
|
|
13655
|
+
* Gets all function implementations provided by all commitments
|
|
13656
|
+
*
|
|
13657
|
+
* Note: This function is intended for server use, there is also equivalent `getAllCommitmentsToolFunctionsForBrowser` for browser use
|
|
13658
|
+
*
|
|
13659
|
+
* @public exported from `@promptbook/node`
|
|
13660
|
+
*/
|
|
13661
|
+
function getAllCommitmentsToolFunctionsForNode() {
|
|
13662
|
+
if (!$isRunningInNode()) {
|
|
13663
|
+
throw new EnvironmentMismatchError(spaceTrim(`
|
|
13664
|
+
Function getAllCommitmentsToolFunctionsForNode should be run in Node.js environment.
|
|
13665
|
+
|
|
13666
|
+
- In browser use getAllCommitmentsToolFunctionsForBrowser instead.
|
|
13667
|
+
- This function can include server-only tools which cannot run in browser environment.
|
|
13668
|
+
|
|
13669
|
+
`));
|
|
13670
|
+
}
|
|
13671
|
+
const allToolFunctionsInBrowser = getAllCommitmentsToolFunctionsForBrowser();
|
|
13672
|
+
const allToolFunctionsInNode = {
|
|
13673
|
+
/**
|
|
13674
|
+
* @@@
|
|
13675
|
+
*
|
|
13676
|
+
* Note: [đē] This function has implementation both for browser and node, this is the full one for node
|
|
13677
|
+
*/
|
|
13678
|
+
async fetch_url_content(args) {
|
|
13679
|
+
console.log('!!!! [Tool] fetch_url_content called', { args });
|
|
13680
|
+
const { url } = args;
|
|
13681
|
+
return await fetchUrlContent(url);
|
|
13682
|
+
},
|
|
13683
|
+
// TODO: !!!! Unhardcode, make proper server function register from definitions
|
|
13684
|
+
};
|
|
13685
|
+
return { ...allToolFunctionsInBrowser, ...allToolFunctionsInNode };
|
|
13686
|
+
}
|
|
13687
|
+
/**
|
|
13688
|
+
* Note: [đĸ] Code in this file should never be never released in packages that could be imported into browser environment
|
|
13689
|
+
* TODO: [đ] Unite `xxxForServer` and `xxxForNode` naming
|
|
12698
13690
|
*/
|
|
12699
13691
|
|
|
12700
13692
|
/**
|
|
@@ -12908,13 +13900,6 @@ class JavascriptEvalExecutionTools {
|
|
|
12908
13900
|
`const ${functionName} = buildinFunctions.${functionName};`)
|
|
12909
13901
|
.join('\n');
|
|
12910
13902
|
// TODO: DRY [đ¯]
|
|
12911
|
-
const commitmentsFunctions = getAllCommitmentsToolFunctions();
|
|
12912
|
-
const commitmentsFunctionsStatement = Object.keys(commitmentsFunctions)
|
|
12913
|
-
.map((functionName) =>
|
|
12914
|
-
// Note: Custom functions are exposed to the current scope as variables
|
|
12915
|
-
`const ${functionName} = commitmentsFunctions.${functionName};`)
|
|
12916
|
-
.join('\n');
|
|
12917
|
-
// TODO: DRY [đ¯]
|
|
12918
13903
|
const customFunctions = this.options.functions || {};
|
|
12919
13904
|
const customFunctionsStatement = Object.keys(customFunctions)
|
|
12920
13905
|
.map((functionName) =>
|
|
@@ -12928,10 +13913,6 @@ class JavascriptEvalExecutionTools {
|
|
|
12928
13913
|
// Build-in functions:
|
|
12929
13914
|
${block(buildinFunctionsStatement)}
|
|
12930
13915
|
|
|
12931
|
-
// Commitments functions:
|
|
12932
|
-
${block(commitmentsFunctionsStatement)}
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
13916
|
// Custom functions:
|
|
12936
13917
|
${block(customFunctionsStatement || '// -- No custom functions --')}
|
|
12937
13918
|
|
|
@@ -13029,10 +14010,11 @@ async function $provideScriptingForNode(options) {
|
|
|
13029
14010
|
throw new EnvironmentMismatchError('Function `$provideScriptingForNode` works only in Node.js environment');
|
|
13030
14011
|
}
|
|
13031
14012
|
// TODO: [đą] Do here auto-installation
|
|
13032
|
-
return [new JavascriptExecutionTools(options)];
|
|
14013
|
+
return [new JavascriptExecutionTools({ ...options, functions: { ...getAllCommitmentsToolFunctionsForNode() } })];
|
|
13033
14014
|
}
|
|
13034
14015
|
/**
|
|
13035
14016
|
* Note: [đĸ] Code in this file should never be never released in packages that could be imported into browser environment
|
|
14017
|
+
* TODO: [đ] Unite `xxxForServer` and `xxxForNode` naming
|
|
13036
14018
|
*/
|
|
13037
14019
|
|
|
13038
14020
|
// TODO: [đĨē] List running services from REMOTE_SERVER_URLS
|