@promptbook/node 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 +2487 -1499
- 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 +2487 -1501
- package/umd/index.umd.js.map +1 -1
package/esm/index.es.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import colors from 'colors';
|
|
2
|
-
import { stat, access, constants, readFile, writeFile, readdir,
|
|
2
|
+
import { mkdir, rm, stat, access, constants, readFile, writeFile, readdir, watch, unlink } from 'fs/promises';
|
|
3
3
|
import { basename, join, dirname, isAbsolute, relative } from 'path';
|
|
4
4
|
import spaceTrim$2, { spaceTrim as spaceTrim$1 } from 'spacetrim';
|
|
5
5
|
import JSZip from 'jszip';
|
|
@@ -11,9 +11,12 @@ import hexEncoder from 'crypto-js/enc-hex';
|
|
|
11
11
|
import sha256 from 'crypto-js/sha256';
|
|
12
12
|
import { SHA256 } from 'crypto-js';
|
|
13
13
|
import { lookup, extension } from 'mime-types';
|
|
14
|
+
import { Readability } from '@mozilla/readability';
|
|
15
|
+
import { JSDOM } from 'jsdom';
|
|
16
|
+
import { Converter } from 'showdown';
|
|
17
|
+
import moment from 'moment';
|
|
14
18
|
import { spawn } from 'child_process';
|
|
15
19
|
import * as dotenv from 'dotenv';
|
|
16
|
-
import moment from 'moment';
|
|
17
20
|
|
|
18
21
|
// ⚠️ WARNING: This code has been generated so that any manual changes will be overwritten
|
|
19
22
|
/**
|
|
@@ -29,7 +32,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
29
32
|
* @generated
|
|
30
33
|
* @see https://github.com/webgptorg/promptbook
|
|
31
34
|
*/
|
|
32
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.105.0-
|
|
35
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.105.0-23';
|
|
33
36
|
/**
|
|
34
37
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
35
38
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -10857,15 +10860,15 @@ async function compilePipeline(pipelineString, tools, options) {
|
|
|
10857
10860
|
*/
|
|
10858
10861
|
|
|
10859
10862
|
/**
|
|
10860
|
-
* Detects if the code is running in a
|
|
10863
|
+
* Detects if the code is running in a browser environment in main thread (Not in a web worker)
|
|
10861
10864
|
*
|
|
10862
10865
|
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
10863
10866
|
*
|
|
10864
10867
|
* @public exported from `@promptbook/utils`
|
|
10865
10868
|
*/
|
|
10866
|
-
function $
|
|
10869
|
+
function $isRunningInBrowser() {
|
|
10867
10870
|
try {
|
|
10868
|
-
return typeof
|
|
10871
|
+
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
10869
10872
|
}
|
|
10870
10873
|
catch (e) {
|
|
10871
10874
|
return false;
|
|
@@ -10876,1535 +10879,1274 @@ function $isRunningInNode() {
|
|
|
10876
10879
|
*/
|
|
10877
10880
|
|
|
10878
10881
|
/**
|
|
10879
|
-
*
|
|
10882
|
+
* Detects if the code is running in a Node.js environment
|
|
10880
10883
|
*
|
|
10881
|
-
* Note: `$` is used to indicate that this function
|
|
10884
|
+
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
10882
10885
|
*
|
|
10883
|
-
* @
|
|
10886
|
+
* @public exported from `@promptbook/utils`
|
|
10884
10887
|
*/
|
|
10885
|
-
function $
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
let cwd;
|
|
10889
|
-
let crashOnError;
|
|
10890
|
-
let args = [];
|
|
10891
|
-
let timeout;
|
|
10892
|
-
let isVerbose;
|
|
10893
|
-
let env;
|
|
10894
|
-
if (typeof options === 'string') {
|
|
10895
|
-
// TODO: [1] DRY default values
|
|
10896
|
-
command = options;
|
|
10897
|
-
cwd = process.cwd();
|
|
10898
|
-
crashOnError = true;
|
|
10899
|
-
timeout = Infinity; // <- TODO: [⏳]
|
|
10900
|
-
isVerbose = DEFAULT_IS_VERBOSE;
|
|
10901
|
-
env = undefined;
|
|
10902
|
-
}
|
|
10903
|
-
else {
|
|
10904
|
-
/*
|
|
10905
|
-
TODO:
|
|
10906
|
-
if ((options as any).commands !== undefined) {
|
|
10907
|
-
commands = (options as any).commands;
|
|
10908
|
-
} else {
|
|
10909
|
-
commands = [(options as any).command];
|
|
10910
|
-
}
|
|
10911
|
-
*/
|
|
10912
|
-
// TODO: [1] DRY default values
|
|
10913
|
-
command = options.command;
|
|
10914
|
-
cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
|
|
10915
|
-
crashOnError = (_b = options.crashOnError) !== null && _b !== void 0 ? _b : true;
|
|
10916
|
-
timeout = (_c = options.timeout) !== null && _c !== void 0 ? _c : Infinity;
|
|
10917
|
-
isVerbose = (_d = options.isVerbose) !== null && _d !== void 0 ? _d : DEFAULT_IS_VERBOSE;
|
|
10918
|
-
env = options.env;
|
|
10919
|
-
}
|
|
10920
|
-
// TODO: /(-[a-zA-Z0-9-]+\s+[^\s]*)|[^\s]*/g
|
|
10921
|
-
const _ = Array.from(command.matchAll(/(".*")|([^\s]*)/g))
|
|
10922
|
-
.map(([match]) => match)
|
|
10923
|
-
.filter((arg) => arg !== '');
|
|
10924
|
-
if (_.length > 1) {
|
|
10925
|
-
[command, ...args] = _;
|
|
10926
|
-
}
|
|
10927
|
-
if (options.args) {
|
|
10928
|
-
args = [...args, ...options.args];
|
|
10929
|
-
}
|
|
10930
|
-
let humanReadableCommand = !['npx', 'npm'].includes(command) ? command : args[0];
|
|
10931
|
-
if (['ts-node'].includes(humanReadableCommand)) {
|
|
10932
|
-
humanReadableCommand += ` ${args[1]}`;
|
|
10933
|
-
}
|
|
10934
|
-
if (/^win/.test(process.platform) && ['npm', 'npx'].includes(command)) {
|
|
10935
|
-
command = `${command}.cmd`;
|
|
10888
|
+
function $isRunningInNode() {
|
|
10889
|
+
try {
|
|
10890
|
+
return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
|
10936
10891
|
}
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
// TODO: This should show type error> execCommandNormalizeOptions({ command: '', commands: [''] });
|
|
10940
|
-
|
|
10941
|
-
/**
|
|
10942
|
-
* Run one command in a shell
|
|
10943
|
-
*
|
|
10944
|
-
*
|
|
10945
|
-
* Note: There are 2 similar functions in the codebase:
|
|
10946
|
-
* - `$execCommand` which runs a single command
|
|
10947
|
-
* - `$execCommands` which runs multiple commands
|
|
10948
|
-
* Note: `$` is used to indicate that this function is not a pure function - it runs a command in a shell
|
|
10949
|
-
*
|
|
10950
|
-
* @public exported from `@promptbook/node`
|
|
10951
|
-
*/
|
|
10952
|
-
function $execCommand(options) {
|
|
10953
|
-
if (!$isRunningInNode()) {
|
|
10954
|
-
throw new EnvironmentMismatchError('Function `$execCommand` can run only in Node environment.js');
|
|
10892
|
+
catch (e) {
|
|
10893
|
+
return false;
|
|
10955
10894
|
}
|
|
10956
|
-
return new Promise((resolve, reject) => {
|
|
10957
|
-
// eslint-disable-next-line prefer-const
|
|
10958
|
-
const { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose = DEFAULT_IS_VERBOSE, env, } = $execCommandNormalizeOptions(options);
|
|
10959
|
-
if (timeout !== Infinity) {
|
|
10960
|
-
// TODO: In waitasecond forTime(Infinity) should be equivalent to forEver()
|
|
10961
|
-
forTime(timeout).then(() => {
|
|
10962
|
-
if (crashOnError) {
|
|
10963
|
-
reject(new Error(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms`));
|
|
10964
|
-
}
|
|
10965
|
-
else {
|
|
10966
|
-
console.warn(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms but continues running`);
|
|
10967
|
-
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
10968
|
-
resolve('Command exceeded time limit');
|
|
10969
|
-
}
|
|
10970
|
-
});
|
|
10971
|
-
}
|
|
10972
|
-
if (isVerbose) {
|
|
10973
|
-
console.info(colors.yellow(cwd) + ' ' + colors.green(command) + ' ' + colors.blue(args.join(' ')));
|
|
10974
|
-
}
|
|
10975
|
-
try {
|
|
10976
|
-
const commandProcess = spawn(command, args, {
|
|
10977
|
-
cwd,
|
|
10978
|
-
shell: true,
|
|
10979
|
-
env: env ? { ...process.env, ...env } : process.env,
|
|
10980
|
-
});
|
|
10981
|
-
if (isVerbose) {
|
|
10982
|
-
commandProcess.on('message', (message) => {
|
|
10983
|
-
console.info({ message });
|
|
10984
|
-
});
|
|
10985
|
-
}
|
|
10986
|
-
const output = [];
|
|
10987
|
-
commandProcess.stdout.on('data', (stdout) => {
|
|
10988
|
-
output.push(stdout.toString());
|
|
10989
|
-
if (isVerbose) {
|
|
10990
|
-
console.info(stdout.toString());
|
|
10991
|
-
}
|
|
10992
|
-
});
|
|
10993
|
-
commandProcess.stderr.on('data', (stderr) => {
|
|
10994
|
-
output.push(stderr.toString());
|
|
10995
|
-
if (isVerbose && stderr.toString().trim()) {
|
|
10996
|
-
console.warn(stderr.toString());
|
|
10997
|
-
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
10998
|
-
}
|
|
10999
|
-
});
|
|
11000
|
-
const finishWithCode = (code) => {
|
|
11001
|
-
if (code !== 0) {
|
|
11002
|
-
if (crashOnError) {
|
|
11003
|
-
reject(new Error(output.join('\n').trim() ||
|
|
11004
|
-
`Command "${humanReadableCommand}" exited with code ${code}`));
|
|
11005
|
-
}
|
|
11006
|
-
else {
|
|
11007
|
-
if (isVerbose) {
|
|
11008
|
-
console.warn(`Command "${humanReadableCommand}" exited with code ${code}`);
|
|
11009
|
-
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
11010
|
-
}
|
|
11011
|
-
resolve(spaceTrim$1(output.join('\n')));
|
|
11012
|
-
}
|
|
11013
|
-
}
|
|
11014
|
-
else {
|
|
11015
|
-
resolve(spaceTrim$1(output.join('\n')));
|
|
11016
|
-
}
|
|
11017
|
-
};
|
|
11018
|
-
commandProcess.on('close', finishWithCode);
|
|
11019
|
-
commandProcess.on('exit', finishWithCode);
|
|
11020
|
-
commandProcess.on('disconnect', () => {
|
|
11021
|
-
// Note: Unexpected disconnection should always result in rejection
|
|
11022
|
-
reject(new Error(`Command "${humanReadableCommand}" disconnected`));
|
|
11023
|
-
});
|
|
11024
|
-
commandProcess.on('error', (error) => {
|
|
11025
|
-
if (crashOnError) {
|
|
11026
|
-
reject(new Error(`Command "${humanReadableCommand}" failed: \n${error.message}`));
|
|
11027
|
-
}
|
|
11028
|
-
else {
|
|
11029
|
-
if (isVerbose) {
|
|
11030
|
-
console.warn(error);
|
|
11031
|
-
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
11032
|
-
}
|
|
11033
|
-
resolve(spaceTrim$1(output.join('\n')));
|
|
11034
|
-
}
|
|
11035
|
-
});
|
|
11036
|
-
}
|
|
11037
|
-
catch (error) {
|
|
11038
|
-
// Note: Unexpected error in sync code should always result in rejection
|
|
11039
|
-
reject(error);
|
|
11040
|
-
}
|
|
11041
|
-
});
|
|
11042
10895
|
}
|
|
11043
10896
|
/**
|
|
11044
|
-
*
|
|
10897
|
+
* TODO: [🎺]
|
|
11045
10898
|
*/
|
|
11046
10899
|
|
|
11047
10900
|
/**
|
|
11048
|
-
*
|
|
11049
|
-
* Returns the path to the executable if found, or null otherwise.
|
|
10901
|
+
* Detects if the code is running in a web worker
|
|
11050
10902
|
*
|
|
11051
|
-
*
|
|
10903
|
+
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
10904
|
+
*
|
|
10905
|
+
* @public exported from `@promptbook/utils`
|
|
11052
10906
|
*/
|
|
11053
|
-
|
|
10907
|
+
function $isRunningInWebWorker() {
|
|
11054
10908
|
try {
|
|
11055
|
-
|
|
11056
|
-
|
|
10909
|
+
// Note: Check for importScripts which is specific to workers
|
|
10910
|
+
// and not available in the main browser thread
|
|
10911
|
+
return (typeof self !== 'undefined' &&
|
|
10912
|
+
typeof self.importScripts === 'function');
|
|
11057
10913
|
}
|
|
11058
|
-
catch (
|
|
11059
|
-
|
|
11060
|
-
return null;
|
|
10914
|
+
catch (e) {
|
|
10915
|
+
return false;
|
|
11061
10916
|
}
|
|
11062
10917
|
}
|
|
11063
10918
|
/**
|
|
11064
|
-
* TODO: [
|
|
11065
|
-
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
10919
|
+
* TODO: [🎺]
|
|
11066
10920
|
*/
|
|
11067
10921
|
|
|
11068
10922
|
/**
|
|
11069
|
-
*
|
|
11070
|
-
* Creates a standardized filesystem interface that scrapers can use for file operations.
|
|
10923
|
+
* Computes SHA-256 hash of the given object
|
|
11071
10924
|
*
|
|
11072
|
-
* @public exported from `@promptbook/
|
|
10925
|
+
* @public exported from `@promptbook/utils`
|
|
11073
10926
|
*/
|
|
11074
|
-
function
|
|
11075
|
-
|
|
11076
|
-
throw new EnvironmentMismatchError('Function `$provideFilesystemForNode` works only in Node.js environment');
|
|
11077
|
-
}
|
|
11078
|
-
return {
|
|
11079
|
-
stat,
|
|
11080
|
-
access,
|
|
11081
|
-
constants,
|
|
11082
|
-
readFile,
|
|
11083
|
-
writeFile,
|
|
11084
|
-
readdir,
|
|
11085
|
-
mkdir,
|
|
11086
|
-
watch,
|
|
11087
|
-
};
|
|
10927
|
+
function computeHash(value) {
|
|
10928
|
+
return SHA256(hexEncoder.parse(spaceTrim$2(valueToString(value)))).toString( /* hex */);
|
|
11088
10929
|
}
|
|
11089
10930
|
/**
|
|
11090
|
-
*
|
|
10931
|
+
* TODO: [🥬][🥬] Use this ACRY
|
|
11091
10932
|
*/
|
|
11092
10933
|
|
|
11093
10934
|
/**
|
|
11094
|
-
*
|
|
10935
|
+
* Makes first letter of a string lowercase
|
|
11095
10936
|
*
|
|
11096
|
-
*
|
|
10937
|
+
* Note: [🔂] This function is idempotent.
|
|
10938
|
+
*
|
|
10939
|
+
* @public exported from `@promptbook/utils`
|
|
11097
10940
|
*/
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
await fs.access(path, fs.constants.X_OK);
|
|
11101
|
-
return true;
|
|
11102
|
-
}
|
|
11103
|
-
catch (error) {
|
|
11104
|
-
return false;
|
|
11105
|
-
}
|
|
10941
|
+
function decapitalize(word) {
|
|
10942
|
+
return word.substring(0, 1).toLowerCase() + word.substring(1);
|
|
11106
10943
|
}
|
|
11107
|
-
/**
|
|
11108
|
-
* Note: Not [~🟢~] because it is not directly dependent on `fs
|
|
11109
|
-
* TODO: [🖇] What about symlinks?
|
|
11110
|
-
*/
|
|
11111
10944
|
|
|
11112
|
-
// Note: Module `userhome` has no types available, so it is imported using `require`
|
|
11113
|
-
// @see https://stackoverflow.com/questions/37000981/how-to-import-node-module-in-typescript-without-type-definitions
|
|
11114
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
11115
|
-
const userhome = require('userhome');
|
|
11116
10945
|
/**
|
|
11117
|
-
*
|
|
11118
|
-
* Returns the path to the executable if found, or null otherwise.
|
|
10946
|
+
* Parses keywords from a string
|
|
11119
10947
|
*
|
|
11120
|
-
* @
|
|
10948
|
+
* @param {string} input
|
|
10949
|
+
* @returns {Set} of keywords without diacritics in lowercase
|
|
10950
|
+
* @public exported from `@promptbook/utils`
|
|
11121
10951
|
*/
|
|
11122
|
-
|
|
11123
|
-
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
return regPath;
|
|
11129
|
-
}
|
|
11130
|
-
else if (await isExecutable(altPath, $provideFilesystemForNode())) {
|
|
11131
|
-
return altPath;
|
|
11132
|
-
}
|
|
11133
|
-
const result = await $execCommand({
|
|
11134
|
-
crashOnError: true,
|
|
11135
|
-
command: `mdfind 'kMDItemDisplayName == "${macOsName}" && kMDItemKind == Application'`,
|
|
11136
|
-
});
|
|
11137
|
-
return result.trim() + toExec;
|
|
11138
|
-
}
|
|
11139
|
-
catch (error) {
|
|
11140
|
-
assertsError(error);
|
|
11141
|
-
return null;
|
|
11142
|
-
}
|
|
10952
|
+
function parseKeywordsFromString(input) {
|
|
10953
|
+
const keywords = normalizeTo_SCREAMING_CASE(removeDiacritics(input))
|
|
10954
|
+
.toLowerCase()
|
|
10955
|
+
.split(/[^a-z0-9]+/gs)
|
|
10956
|
+
.filter((value) => value);
|
|
10957
|
+
return new Set(keywords);
|
|
11143
10958
|
}
|
|
10959
|
+
|
|
11144
10960
|
/**
|
|
11145
|
-
*
|
|
11146
|
-
*
|
|
10961
|
+
* Converts a name string into a URI-compatible format.
|
|
10962
|
+
*
|
|
10963
|
+
* @param name The string to be converted to a URI-compatible format.
|
|
10964
|
+
* @returns A URI-compatible string derived from the input name.
|
|
10965
|
+
* @example 'Hello World' -> 'hello-world'
|
|
10966
|
+
* @public exported from `@promptbook/utils`
|
|
11147
10967
|
*/
|
|
10968
|
+
function nameToUriPart(name) {
|
|
10969
|
+
let uriPart = name;
|
|
10970
|
+
uriPart = uriPart.toLowerCase();
|
|
10971
|
+
uriPart = removeDiacritics(uriPart);
|
|
10972
|
+
uriPart = uriPart.replace(/[^a-zA-Z0-9]+/g, '-');
|
|
10973
|
+
uriPart = uriPart.replace(/^-+/, '');
|
|
10974
|
+
uriPart = uriPart.replace(/-+$/, '');
|
|
10975
|
+
return uriPart;
|
|
10976
|
+
}
|
|
11148
10977
|
|
|
11149
10978
|
/**
|
|
11150
|
-
*
|
|
11151
|
-
* Returns the path to the executable if found, or null otherwise.
|
|
10979
|
+
* Converts a given name into URI-compatible parts.
|
|
11152
10980
|
*
|
|
11153
|
-
* @
|
|
10981
|
+
* @param name The name to be converted into URI parts.
|
|
10982
|
+
* @returns An array of URI-compatible parts derived from the name.
|
|
10983
|
+
* @example 'Example Name' -> ['example', 'name']
|
|
10984
|
+
* @public exported from `@promptbook/utils`
|
|
11154
10985
|
*/
|
|
11155
|
-
|
|
11156
|
-
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
join(process.env.LOCALAPPDATA || '', 'Programs'),
|
|
11160
|
-
process.env.PROGRAMFILES,
|
|
11161
|
-
process.env['PROGRAMFILES(X86)'],
|
|
11162
|
-
];
|
|
11163
|
-
for (const prefix of prefixes) {
|
|
11164
|
-
const path = prefix + windowsSuffix;
|
|
11165
|
-
if (await isExecutable(path, $provideFilesystemForNode())) {
|
|
11166
|
-
return path;
|
|
11167
|
-
}
|
|
11168
|
-
}
|
|
11169
|
-
throw new Error(`Can not locate app ${appName} on Windows.`);
|
|
11170
|
-
}
|
|
11171
|
-
catch (error) {
|
|
11172
|
-
assertsError(error);
|
|
11173
|
-
return null;
|
|
11174
|
-
}
|
|
10986
|
+
function nameToUriParts(name) {
|
|
10987
|
+
return nameToUriPart(name)
|
|
10988
|
+
.split('-')
|
|
10989
|
+
.filter((value) => value !== '');
|
|
11175
10990
|
}
|
|
10991
|
+
|
|
11176
10992
|
/**
|
|
11177
|
-
*
|
|
11178
|
-
*
|
|
10993
|
+
* Normalizes a given text to PascalCase format.
|
|
10994
|
+
*
|
|
10995
|
+
* Note: [🔂] This function is idempotent.
|
|
10996
|
+
*
|
|
10997
|
+
* @param text @public exported from `@promptbook/utils`
|
|
10998
|
+
* @returns
|
|
10999
|
+
* @example 'HelloWorld'
|
|
11000
|
+
* @example 'ILovePromptbook'
|
|
11001
|
+
* @public exported from `@promptbook/utils`
|
|
11179
11002
|
*/
|
|
11003
|
+
function normalizeTo_PascalCase(text) {
|
|
11004
|
+
return normalizeTo_camelCase(text, true);
|
|
11005
|
+
}
|
|
11180
11006
|
|
|
11181
11007
|
/**
|
|
11182
|
-
*
|
|
11008
|
+
* Take every whitespace (space, new line, tab) and replace it with a single space
|
|
11183
11009
|
*
|
|
11184
|
-
*
|
|
11010
|
+
* Note: [🔂] This function is idempotent.
|
|
11011
|
+
*
|
|
11012
|
+
* @public exported from `@promptbook/utils`
|
|
11185
11013
|
*/
|
|
11186
|
-
function
|
|
11187
|
-
|
|
11188
|
-
|
|
11014
|
+
function normalizeWhitespaces(sentence) {
|
|
11015
|
+
return sentence.replace(/\s+/gs, ' ').trim();
|
|
11016
|
+
}
|
|
11017
|
+
|
|
11018
|
+
/**
|
|
11019
|
+
* Removes quotes and optional introduce text from a string
|
|
11020
|
+
*
|
|
11021
|
+
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
11022
|
+
* Note: This function trims the text and removes whole introduce sentence if it is present
|
|
11023
|
+
* Note: There are two similar functions:
|
|
11024
|
+
* - `removeQuotes` which removes only bounding quotes
|
|
11025
|
+
* - `unwrapResult` which removes whole introduce sentence
|
|
11026
|
+
*
|
|
11027
|
+
* @param text optionally quoted text
|
|
11028
|
+
* @returns text without quotes
|
|
11029
|
+
* @public exported from `@promptbook/utils`
|
|
11030
|
+
*/
|
|
11031
|
+
function unwrapResult(text, options) {
|
|
11032
|
+
const { isTrimmed = true, isIntroduceSentenceRemoved = true } = options || {};
|
|
11033
|
+
let trimmedText = text;
|
|
11034
|
+
// Remove leading and trailing spaces and newlines
|
|
11035
|
+
if (isTrimmed) {
|
|
11036
|
+
trimmedText = spaceTrim$1(trimmedText);
|
|
11189
11037
|
}
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11038
|
+
let processedText = trimmedText;
|
|
11039
|
+
// Check for markdown code block
|
|
11040
|
+
const codeBlockRegex = /^```[a-z]*\n([\s\S]*?)\n```\s*$/;
|
|
11041
|
+
const codeBlockMatch = processedText.match(codeBlockRegex);
|
|
11042
|
+
if (codeBlockMatch && codeBlockMatch[1] !== undefined) {
|
|
11043
|
+
// Check if there's only one code block
|
|
11044
|
+
const codeBlockCount = (processedText.match(/```/g) || []).length / 2;
|
|
11045
|
+
if (codeBlockCount === 1) {
|
|
11046
|
+
return unwrapResult(codeBlockMatch[1], { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
11197
11047
|
}
|
|
11198
11048
|
}
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
|
|
11049
|
+
if (isIntroduceSentenceRemoved) {
|
|
11050
|
+
const introduceSentenceRegex = /^[a-zěščřžýáíéúů:\s]*:\s*/i;
|
|
11051
|
+
if (introduceSentenceRegex.test(text)) {
|
|
11052
|
+
// Remove the introduce sentence and quotes by replacing it with an empty string
|
|
11053
|
+
processedText = processedText.replace(introduceSentenceRegex, '');
|
|
11202
11054
|
}
|
|
11203
|
-
|
|
11204
|
-
|
|
11055
|
+
processedText = spaceTrim$1(processedText);
|
|
11056
|
+
// Check again for code block after removing introduce sentence
|
|
11057
|
+
const codeBlockMatch2 = processedText.match(codeBlockRegex);
|
|
11058
|
+
if (codeBlockMatch2 && codeBlockMatch2[1] !== undefined) {
|
|
11059
|
+
const codeBlockCount = (processedText.match(/```/g) || []).length / 2;
|
|
11060
|
+
if (codeBlockCount === 1) {
|
|
11061
|
+
return unwrapResult(codeBlockMatch2[1], { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
11062
|
+
}
|
|
11205
11063
|
}
|
|
11206
11064
|
}
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11065
|
+
if (processedText.length < 3) {
|
|
11066
|
+
return trimmedText;
|
|
11067
|
+
}
|
|
11068
|
+
if (processedText.includes('\n')) {
|
|
11069
|
+
return trimmedText;
|
|
11070
|
+
}
|
|
11071
|
+
// Remove the quotes by extracting the substring without the first and last characters
|
|
11072
|
+
const unquotedText = processedText.slice(1, -1);
|
|
11073
|
+
// Check if the text starts and ends with quotes
|
|
11074
|
+
if ([
|
|
11075
|
+
['"', '"'],
|
|
11076
|
+
["'", "'"],
|
|
11077
|
+
['`', '`'],
|
|
11078
|
+
['*', '*'],
|
|
11079
|
+
['_', '_'],
|
|
11080
|
+
['„', '“'],
|
|
11081
|
+
['«', '»'] /* <- QUOTES to config */,
|
|
11082
|
+
].some(([startQuote, endQuote]) => {
|
|
11083
|
+
if (!processedText.startsWith(startQuote)) {
|
|
11084
|
+
return false;
|
|
11210
11085
|
}
|
|
11211
|
-
|
|
11212
|
-
|
|
11086
|
+
if (!processedText.endsWith(endQuote)) {
|
|
11087
|
+
return false;
|
|
11088
|
+
}
|
|
11089
|
+
if (unquotedText.includes(startQuote) && !unquotedText.includes(endQuote)) {
|
|
11090
|
+
return false;
|
|
11091
|
+
}
|
|
11092
|
+
if (!unquotedText.includes(startQuote) && unquotedText.includes(endQuote)) {
|
|
11093
|
+
return false;
|
|
11213
11094
|
}
|
|
11095
|
+
return true;
|
|
11096
|
+
})) {
|
|
11097
|
+
return unwrapResult(unquotedText, { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
11098
|
+
}
|
|
11099
|
+
else {
|
|
11100
|
+
return processedText;
|
|
11214
11101
|
}
|
|
11215
11102
|
}
|
|
11216
11103
|
/**
|
|
11217
|
-
* TODO: [🧠]
|
|
11218
|
-
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
11104
|
+
* TODO: [🧠] Should this also unwrap the (parenthesis)
|
|
11219
11105
|
*/
|
|
11220
11106
|
|
|
11107
|
+
// <- TODO: Auto convert to type `import { ... } from 'type-fest';`
|
|
11221
11108
|
/**
|
|
11222
|
-
*
|
|
11223
|
-
* Returns the path to the executable if found, or null otherwise.
|
|
11109
|
+
* Tests if the value is [🚉] serializable as JSON
|
|
11224
11110
|
*
|
|
11225
|
-
*
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
*
|
|
11237
|
-
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
11238
|
-
*/
|
|
11239
|
-
|
|
11240
|
-
/**
|
|
11241
|
-
* Locates the Pandoc executable on the current system by searching platform-specific paths.
|
|
11242
|
-
* Returns the path to the executable if found, or null otherwise.
|
|
11111
|
+
* - Almost all primitives are serializable BUT:
|
|
11112
|
+
* - `undefined` is not serializable
|
|
11113
|
+
* - `NaN` is not serializable
|
|
11114
|
+
* - Objects and arrays are serializable if all their properties are serializable
|
|
11115
|
+
* - Functions are not serializable
|
|
11116
|
+
* - Circular references are not serializable
|
|
11117
|
+
* - `Date` objects are not serializable
|
|
11118
|
+
* - `Map` and `Set` objects are not serializable
|
|
11119
|
+
* - `RegExp` objects are not serializable
|
|
11120
|
+
* - `Error` objects are not serializable
|
|
11121
|
+
* - `Symbol` objects are not serializable
|
|
11122
|
+
* - And much more...
|
|
11243
11123
|
*
|
|
11244
|
-
*
|
|
11124
|
+
*
|
|
11125
|
+
* @public exported from `@promptbook/utils`
|
|
11245
11126
|
*/
|
|
11246
|
-
function
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
|
|
11127
|
+
function isSerializableAsJson(value) {
|
|
11128
|
+
try {
|
|
11129
|
+
checkSerializableAsJson({ value });
|
|
11130
|
+
return true;
|
|
11131
|
+
}
|
|
11132
|
+
catch (error) {
|
|
11133
|
+
return false;
|
|
11134
|
+
}
|
|
11253
11135
|
}
|
|
11254
11136
|
/**
|
|
11255
|
-
* TODO: [🧠][
|
|
11256
|
-
*
|
|
11137
|
+
* TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
|
|
11138
|
+
* TODO: [🧠][💺] Can be done this on type-level?
|
|
11257
11139
|
*/
|
|
11258
11140
|
|
|
11259
11141
|
/**
|
|
11260
|
-
*
|
|
11142
|
+
* Determines if the given path is a root path.
|
|
11261
11143
|
*
|
|
11262
|
-
*
|
|
11144
|
+
* Note: This does not check if the file exists only if the path is valid
|
|
11145
|
+
* @public exported from `@promptbook/utils`
|
|
11263
11146
|
*/
|
|
11264
|
-
|
|
11265
|
-
if (
|
|
11266
|
-
|
|
11147
|
+
function isRootPath(value) {
|
|
11148
|
+
if (value === '/') {
|
|
11149
|
+
return true;
|
|
11267
11150
|
}
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11272
|
-
};
|
|
11151
|
+
if (/^[A-Z]:\\$/i.test(value)) {
|
|
11152
|
+
return true;
|
|
11153
|
+
}
|
|
11154
|
+
return false;
|
|
11273
11155
|
}
|
|
11274
11156
|
/**
|
|
11275
|
-
* TODO: [
|
|
11276
|
-
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
11157
|
+
* TODO: [🍏] Make for MacOS paths
|
|
11277
11158
|
*/
|
|
11278
11159
|
|
|
11279
11160
|
/**
|
|
11280
|
-
*
|
|
11161
|
+
* Tests if given string is valid agent URL
|
|
11281
11162
|
*
|
|
11282
|
-
* Note:
|
|
11283
|
-
*
|
|
11284
|
-
*
|
|
11163
|
+
* Note: There are few similar functions:
|
|
11164
|
+
* - `isValidUrl` which tests any URL
|
|
11165
|
+
* - `isValidAgentUrl` *(this one)* which tests just agent URL
|
|
11166
|
+
* - `isValidPipelineUrl` which tests just pipeline URL
|
|
11167
|
+
*
|
|
11168
|
+
* @public exported from `@promptbook/utils`
|
|
11285
11169
|
*/
|
|
11286
|
-
|
|
11170
|
+
function isValidAgentUrl(url) {
|
|
11171
|
+
if (!isValidUrl(url)) {
|
|
11172
|
+
return false;
|
|
11173
|
+
}
|
|
11174
|
+
if (!url.startsWith('https://') && !url.startsWith('http://') /* <- Note: [👣] */) {
|
|
11175
|
+
return false;
|
|
11176
|
+
}
|
|
11177
|
+
if (url.includes('#')) {
|
|
11178
|
+
// TODO: [🐠]
|
|
11179
|
+
return false;
|
|
11180
|
+
}
|
|
11181
|
+
/*
|
|
11182
|
+
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
11183
|
+
if (isUrlOnPrivateNetwork(url)) {
|
|
11184
|
+
return false;
|
|
11185
|
+
}
|
|
11186
|
+
*/
|
|
11187
|
+
return true;
|
|
11188
|
+
}
|
|
11287
11189
|
/**
|
|
11288
|
-
* TODO: [
|
|
11190
|
+
* TODO: [🐠] Maybe more info why the URL is invalid
|
|
11289
11191
|
*/
|
|
11290
11192
|
|
|
11291
11193
|
/**
|
|
11292
|
-
*
|
|
11194
|
+
* Retrieves an intermediate source for a scraper based on the knowledge source.
|
|
11195
|
+
* Manages the caching and retrieval of intermediate scraper results for optimized performance.
|
|
11293
11196
|
*
|
|
11294
|
-
*
|
|
11295
|
-
* @public exported from `@promptbook/utils`
|
|
11197
|
+
* @private as internal utility for scrapers
|
|
11296
11198
|
*/
|
|
11297
|
-
function
|
|
11298
|
-
|
|
11299
|
-
|
|
11199
|
+
async function getScraperIntermediateSource(source, options) {
|
|
11200
|
+
const { filename: sourceFilename, url } = source;
|
|
11201
|
+
const { rootDirname, cacheDirname, intermediateFilesStrategy, extension, isVerbose } = options;
|
|
11202
|
+
// TODO: [👬] DRY
|
|
11203
|
+
const hash = SHA256(
|
|
11204
|
+
// <- TODO: [🥬] Encapsulate sha256 to some private utility function
|
|
11205
|
+
hexEncoder.parse(sourceFilename || url || 'untitled'))
|
|
11206
|
+
.toString( /* hex */)
|
|
11207
|
+
.substring(0, 20);
|
|
11208
|
+
// <- TODO: [🥬] Make some system for hashes and ids of promptbook
|
|
11209
|
+
const semanticName = normalizeToKebabCase(titleToName((sourceFilename || url || '').split('intermediate').join(''))).substring(0, 20);
|
|
11210
|
+
// <- TODO: [🐱🐉]
|
|
11211
|
+
const pieces = ['intermediate', semanticName, hash].filter((piece) => piece !== '');
|
|
11212
|
+
const name = pieces.join('-').split('--').join('-');
|
|
11213
|
+
const cacheFilename = join(process.cwd(), cacheDirname, ...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), name)
|
|
11214
|
+
.split('\\')
|
|
11215
|
+
.join('/') +
|
|
11216
|
+
'.' +
|
|
11217
|
+
extension;
|
|
11218
|
+
// Note: Try to create cache directory, but don't fail if filesystem has issues
|
|
11219
|
+
try {
|
|
11220
|
+
await mkdir(dirname(cacheFilename), { recursive: true });
|
|
11300
11221
|
}
|
|
11301
|
-
|
|
11302
|
-
|
|
11222
|
+
catch (error) {
|
|
11223
|
+
// Note: If we can't create cache directory, continue without it
|
|
11224
|
+
// This handles read-only filesystems, permission issues, and missing parent directories
|
|
11225
|
+
if (error instanceof Error &&
|
|
11226
|
+
(error.message.includes('EROFS') ||
|
|
11227
|
+
error.message.includes('read-only') ||
|
|
11228
|
+
error.message.includes('EACCES') ||
|
|
11229
|
+
error.message.includes('EPERM') ||
|
|
11230
|
+
error.message.includes('ENOENT'))) ;
|
|
11231
|
+
else {
|
|
11232
|
+
// Re-throw other unexpected errors
|
|
11233
|
+
throw error;
|
|
11234
|
+
}
|
|
11303
11235
|
}
|
|
11304
|
-
|
|
11236
|
+
let isDestroyed = true;
|
|
11237
|
+
const fileHandler = {
|
|
11238
|
+
filename: cacheFilename,
|
|
11239
|
+
get isDestroyed() {
|
|
11240
|
+
return isDestroyed;
|
|
11241
|
+
},
|
|
11242
|
+
async destroy() {
|
|
11243
|
+
if (intermediateFilesStrategy === 'HIDE_AND_CLEAN') {
|
|
11244
|
+
if (isVerbose) {
|
|
11245
|
+
console.info('legacyDocumentScraper: Clening cache');
|
|
11246
|
+
}
|
|
11247
|
+
await rm(cacheFilename);
|
|
11248
|
+
// TODO: [🐿][🧠] Maybe remove empty folders
|
|
11249
|
+
}
|
|
11250
|
+
isDestroyed = true;
|
|
11251
|
+
},
|
|
11252
|
+
};
|
|
11253
|
+
return fileHandler;
|
|
11305
11254
|
}
|
|
11306
11255
|
/**
|
|
11307
|
-
*
|
|
11256
|
+
* Note: Not using `FileCacheStorage` for two reasons:
|
|
11257
|
+
* 1) Need to store more than serialized JSONs
|
|
11258
|
+
* 2) Need to switch between a `rootDirname` and `cacheDirname` <- TODO: [😡]
|
|
11259
|
+
* TODO: [🐱🐉][🧠] Make some smart crop
|
|
11260
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
11308
11261
|
*/
|
|
11309
11262
|
|
|
11310
11263
|
/**
|
|
11311
|
-
*
|
|
11264
|
+
* Metadata of the scraper
|
|
11265
|
+
*
|
|
11266
|
+
* @private within the scraper directory
|
|
11267
|
+
*/
|
|
11268
|
+
const markdownScraperMetadata = $deepFreeze({
|
|
11269
|
+
title: 'Markdown scraper',
|
|
11270
|
+
packageName: '@promptbook/markdown-utils',
|
|
11271
|
+
className: 'MarkdownScraper',
|
|
11272
|
+
mimeTypes: ['text/markdown', 'text/plain'],
|
|
11273
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
|
|
11274
|
+
isAvailableInBrowser: true,
|
|
11275
|
+
// <- 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
|
|
11276
|
+
requiredExecutables: [],
|
|
11277
|
+
}); /* <- Note: [🤛] */
|
|
11278
|
+
/**
|
|
11279
|
+
* Registration of known scraper metadata
|
|
11280
|
+
*
|
|
11281
|
+
* Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
|
|
11312
11282
|
*
|
|
11313
|
-
* Note: `$` is used to indicate that this interacts with the global scope
|
|
11314
|
-
* @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
|
|
11315
11283
|
* @public exported from `@promptbook/core`
|
|
11284
|
+
* @public exported from `@promptbook/wizard`
|
|
11285
|
+
* @public exported from `@promptbook/cli`
|
|
11316
11286
|
*/
|
|
11317
|
-
|
|
11287
|
+
$scrapersMetadataRegister.register(markdownScraperMetadata);
|
|
11318
11288
|
/**
|
|
11319
|
-
*
|
|
11289
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
11320
11290
|
*/
|
|
11321
11291
|
|
|
11322
11292
|
/**
|
|
11323
|
-
*
|
|
11293
|
+
* Scraper for markdown files
|
|
11324
11294
|
*
|
|
11325
|
-
*
|
|
11295
|
+
* @see `documentationUrl` for more details
|
|
11296
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
11326
11297
|
*/
|
|
11327
|
-
|
|
11298
|
+
class MarkdownScraper {
|
|
11299
|
+
/**
|
|
11300
|
+
* Metadata of the scraper which includes title, mime types, etc.
|
|
11301
|
+
*/
|
|
11302
|
+
get metadata() {
|
|
11303
|
+
return markdownScraperMetadata;
|
|
11304
|
+
}
|
|
11305
|
+
constructor(tools, options) {
|
|
11306
|
+
this.tools = tools;
|
|
11307
|
+
this.options = options;
|
|
11308
|
+
}
|
|
11309
|
+
/**
|
|
11310
|
+
* Scrapes the markdown file and returns the knowledge pieces or `null` if it can't scrape it
|
|
11311
|
+
*/
|
|
11312
|
+
async scrape(source) {
|
|
11313
|
+
const { maxParallelCount = DEFAULT_MAX_PARALLEL_COUNT, isVerbose = DEFAULT_IS_VERBOSE } = this.options;
|
|
11314
|
+
const { llm } = this.tools;
|
|
11315
|
+
if (llm === undefined) {
|
|
11316
|
+
throw new MissingToolsError('LLM tools are required for scraping external files');
|
|
11317
|
+
// <- Note: This scraper is used in all other scrapers, so saying "external files" not "markdown files"
|
|
11318
|
+
}
|
|
11319
|
+
const llmTools = getSingleLlmExecutionTools(llm);
|
|
11320
|
+
// TODO: [🌼] In future use `ptbk make` and made getPipelineCollection
|
|
11321
|
+
const collection = createPipelineCollectionFromJson(...PipelineCollection);
|
|
11322
|
+
const prepareKnowledgeFromMarkdownExecutor = createPipelineExecutor({
|
|
11323
|
+
pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book'),
|
|
11324
|
+
tools: {
|
|
11325
|
+
llm: llm,
|
|
11326
|
+
},
|
|
11327
|
+
});
|
|
11328
|
+
const prepareTitleExecutor = createPipelineExecutor({
|
|
11329
|
+
pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-title.book'),
|
|
11330
|
+
tools: {
|
|
11331
|
+
llm: llm,
|
|
11332
|
+
},
|
|
11333
|
+
});
|
|
11334
|
+
const prepareKeywordsExecutor = createPipelineExecutor({
|
|
11335
|
+
pipeline: await collection.getPipelineByUrl('https://promptbook.studio/promptbook/prepare-knowledge-keywords.book'),
|
|
11336
|
+
tools: {
|
|
11337
|
+
llm: llm,
|
|
11338
|
+
},
|
|
11339
|
+
});
|
|
11340
|
+
const knowledgeContent = await source.asText();
|
|
11341
|
+
const result = await prepareKnowledgeFromMarkdownExecutor({ knowledgeContent }).asPromise({
|
|
11342
|
+
isCrashedOnError: true,
|
|
11343
|
+
});
|
|
11344
|
+
const { outputParameters } = result;
|
|
11345
|
+
const { knowledgePieces: knowledgePiecesRaw } = outputParameters;
|
|
11346
|
+
const knowledgeTextPieces = (knowledgePiecesRaw || '').split('\n---\n');
|
|
11347
|
+
// <- TODO: [main] Smarter split and filter out empty pieces
|
|
11348
|
+
if (isVerbose) {
|
|
11349
|
+
console.info('knowledgeTextPieces:', knowledgeTextPieces);
|
|
11350
|
+
}
|
|
11351
|
+
// const usage = ;
|
|
11352
|
+
const knowledge = await Promise.all(
|
|
11353
|
+
// TODO: [🪂] Do not send all at once but in chunks
|
|
11354
|
+
knowledgeTextPieces.map(async (knowledgeTextPiece, i) => {
|
|
11355
|
+
// Note: These are just default values, they will be overwritten by the actual values:
|
|
11356
|
+
let name = `piece-${i}`;
|
|
11357
|
+
let title = spaceTrim$2(knowledgeTextPiece.substring(0, 100));
|
|
11358
|
+
const knowledgePieceContent = spaceTrim$2(knowledgeTextPiece);
|
|
11359
|
+
let keywords = [];
|
|
11360
|
+
const index = [];
|
|
11361
|
+
/*
|
|
11362
|
+
TODO: [☀] Track line and column of the source
|
|
11363
|
+
const sources: KnowledgePiecePreparedJson['sources'] = [
|
|
11364
|
+
];
|
|
11365
|
+
*/
|
|
11366
|
+
try {
|
|
11367
|
+
const titleResult = await prepareTitleExecutor({ knowledgePieceContent }).asPromise({
|
|
11368
|
+
isCrashedOnError: true,
|
|
11369
|
+
});
|
|
11370
|
+
const { title: titleRaw = 'Untitled' } = titleResult.outputParameters;
|
|
11371
|
+
title = spaceTrim$2(titleRaw) /* <- TODO: Maybe do in pipeline */;
|
|
11372
|
+
name = titleToName(title);
|
|
11373
|
+
// --- Keywords
|
|
11374
|
+
const keywordsResult = await prepareKeywordsExecutor({ knowledgePieceContent }).asPromise({
|
|
11375
|
+
isCrashedOnError: true,
|
|
11376
|
+
});
|
|
11377
|
+
const { keywords: keywordsRaw = '' } = keywordsResult.outputParameters;
|
|
11378
|
+
keywords = (keywordsRaw || '')
|
|
11379
|
+
.split(',')
|
|
11380
|
+
.map((keyword) => keyword.trim())
|
|
11381
|
+
.filter((keyword) => keyword !== '');
|
|
11382
|
+
if (isVerbose) {
|
|
11383
|
+
console.info(`Keywords for "${title}":`, keywords);
|
|
11384
|
+
}
|
|
11385
|
+
// ---
|
|
11386
|
+
if (!llmTools.callEmbeddingModel) {
|
|
11387
|
+
// TODO: [🟥] Detect browser / node and make it colorful
|
|
11388
|
+
console.error('No callEmbeddingModel function provided');
|
|
11389
|
+
}
|
|
11390
|
+
else {
|
|
11391
|
+
// TODO: [🧠][🎛] Embedding via multiple models
|
|
11392
|
+
const embeddingResult = await llmTools.callEmbeddingModel({
|
|
11393
|
+
title: `Embedding for ${title}` /* <- Note: No impact on embedding result itself, just for logging */,
|
|
11394
|
+
parameters: {},
|
|
11395
|
+
content: knowledgePieceContent,
|
|
11396
|
+
modelRequirements: {
|
|
11397
|
+
modelVariant: 'EMBEDDING',
|
|
11398
|
+
},
|
|
11399
|
+
});
|
|
11400
|
+
index.push({
|
|
11401
|
+
modelName: embeddingResult.modelName,
|
|
11402
|
+
position: [...embeddingResult.content],
|
|
11403
|
+
// <- TODO: [🪓] Here should be no need for spreading new array, just `position: embeddingResult.content`
|
|
11404
|
+
});
|
|
11405
|
+
}
|
|
11406
|
+
}
|
|
11407
|
+
catch (error) {
|
|
11408
|
+
// Note: Here is expected error:
|
|
11409
|
+
// > PipelineExecutionError: You have not provided any `LlmExecutionTools` that support model variant "EMBEDDING
|
|
11410
|
+
if (!(error instanceof PipelineExecutionError)) {
|
|
11411
|
+
throw error;
|
|
11412
|
+
}
|
|
11413
|
+
// TODO: [🟥] Detect browser / node and make it colorful
|
|
11414
|
+
console.error(error, "<- Note: This error is not critical to prepare the pipeline, just knowledge pieces won't have embeddings");
|
|
11415
|
+
}
|
|
11416
|
+
return {
|
|
11417
|
+
name,
|
|
11418
|
+
title,
|
|
11419
|
+
content: knowledgePieceContent,
|
|
11420
|
+
keywords,
|
|
11421
|
+
index,
|
|
11422
|
+
// <- TODO: [☀] sources,
|
|
11423
|
+
};
|
|
11424
|
+
}));
|
|
11425
|
+
return knowledge;
|
|
11426
|
+
}
|
|
11427
|
+
}
|
|
11328
11428
|
/**
|
|
11329
|
-
*
|
|
11429
|
+
* TODO: [🪂] Do it in parallel 11:11
|
|
11430
|
+
* Note: No need to aggregate usage here, it is done by intercepting the llmTools
|
|
11431
|
+
*/
|
|
11432
|
+
|
|
11433
|
+
/**
|
|
11434
|
+
* Metadata of the scraper
|
|
11330
11435
|
*
|
|
11331
|
-
*
|
|
11436
|
+
* @private within the scraper directory
|
|
11437
|
+
*/
|
|
11438
|
+
const websiteScraperMetadata = $deepFreeze({
|
|
11439
|
+
title: 'Website scraper',
|
|
11440
|
+
packageName: '@promptbook/website-crawler',
|
|
11441
|
+
className: 'WebsiteScraper',
|
|
11442
|
+
mimeTypes: ['text/html'],
|
|
11443
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
|
|
11444
|
+
isAvailableInBrowser: false,
|
|
11445
|
+
// <- Note: [🌏] Only `MarkdownScraper` makes sense to be available in the browser, for scraping non-markdown sources in the browser use a remote server
|
|
11446
|
+
requiredExecutables: [],
|
|
11447
|
+
}); /* <- Note: [🤛] */
|
|
11448
|
+
/**
|
|
11449
|
+
* Registration of known scraper metadata
|
|
11332
11450
|
*
|
|
11333
|
-
*
|
|
11451
|
+
* Warning: This is not useful for the end user, it is just a side effect of the mechanism that handles all available known scrapers
|
|
11452
|
+
*
|
|
11453
|
+
* @public exported from `@promptbook/core`
|
|
11454
|
+
* @public exported from `@promptbook/wizard`
|
|
11455
|
+
* @public exported from `@promptbook/cli`
|
|
11334
11456
|
*/
|
|
11335
|
-
|
|
11336
|
-
$usedEnvFilename = filepath;
|
|
11337
|
-
}
|
|
11457
|
+
$scrapersMetadataRegister.register(websiteScraperMetadata);
|
|
11338
11458
|
/**
|
|
11339
|
-
*
|
|
11459
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
11460
|
+
*/
|
|
11461
|
+
|
|
11462
|
+
/**
|
|
11463
|
+
* Create a new showdown converter instance
|
|
11340
11464
|
*
|
|
11341
|
-
*
|
|
11465
|
+
* @private utility of `WebsiteScraper`
|
|
11466
|
+
*/
|
|
11467
|
+
function createShowdownConverter() {
|
|
11468
|
+
return new Converter({
|
|
11469
|
+
flavor: 'github',
|
|
11470
|
+
/*
|
|
11471
|
+
> import showdownHighlight from 'showdown-highlight';
|
|
11472
|
+
> extensions: [
|
|
11473
|
+
> showdownHighlight({
|
|
11474
|
+
> // Whether to add the classes to the <pre> tag, default is false
|
|
11475
|
+
> pre: true,
|
|
11476
|
+
> // Whether to use hljs' auto language detection, default is true
|
|
11477
|
+
> auto_detection: true,
|
|
11478
|
+
> }),
|
|
11479
|
+
> ],
|
|
11480
|
+
*/
|
|
11481
|
+
});
|
|
11482
|
+
}
|
|
11483
|
+
|
|
11484
|
+
// TODO: [🏳🌈] Finally take pick of .json vs .ts
|
|
11485
|
+
/**
|
|
11486
|
+
* Scraper for websites
|
|
11342
11487
|
*
|
|
11343
|
-
* @
|
|
11488
|
+
* @see `documentationUrl` for more details
|
|
11489
|
+
* @public exported from `@promptbook/website-crawler`
|
|
11344
11490
|
*/
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
|
|
11491
|
+
class WebsiteScraper {
|
|
11492
|
+
/**
|
|
11493
|
+
* Metadata of the scraper which includes title, mime types, etc.
|
|
11494
|
+
*/
|
|
11495
|
+
get metadata() {
|
|
11496
|
+
return websiteScraperMetadata;
|
|
11350
11497
|
}
|
|
11351
|
-
|
|
11352
|
-
|
|
11498
|
+
constructor(tools, options) {
|
|
11499
|
+
this.tools = tools;
|
|
11500
|
+
this.options = options;
|
|
11501
|
+
this.markdownScraper = new MarkdownScraper(tools, options);
|
|
11502
|
+
this.showdownConverter = createShowdownConverter();
|
|
11353
11503
|
}
|
|
11354
11504
|
/**
|
|
11355
|
-
*
|
|
11505
|
+
* Convert the website to `.md` file and returns intermediate source
|
|
11506
|
+
*
|
|
11507
|
+
* 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
|
|
11356
11508
|
*/
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11509
|
+
async $convert(source) {
|
|
11510
|
+
const {
|
|
11511
|
+
// TODO: [🧠] Maybe in node use headless browser not just JSDOM
|
|
11512
|
+
rootDirname = process.cwd(), cacheDirname = DEFAULT_SCRAPE_CACHE_DIRNAME, intermediateFilesStrategy = DEFAULT_INTERMEDIATE_FILES_STRATEGY, isVerbose = DEFAULT_IS_VERBOSE, } = this.options;
|
|
11513
|
+
if (source.url === null) {
|
|
11514
|
+
throw new KnowledgeScrapeError('Website scraper requires URL');
|
|
11361
11515
|
}
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
for (const { packageName, className } of $llmToolsRegister.list()) {
|
|
11365
|
-
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
11366
|
-
continue;
|
|
11516
|
+
if (this.tools.fs === undefined) {
|
|
11517
|
+
throw new EnvironmentMismatchError('Can not scrape websites without filesystem tools');
|
|
11367
11518
|
}
|
|
11368
|
-
|
|
11519
|
+
const jsdom = new JSDOM(await source.asText(), {
|
|
11520
|
+
url: source.url,
|
|
11521
|
+
});
|
|
11522
|
+
const reader = new Readability(jsdom.window.document);
|
|
11523
|
+
const article = reader.parse();
|
|
11524
|
+
// console.log(article);
|
|
11525
|
+
// await forTime(10000);
|
|
11526
|
+
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;
|
|
11527
|
+
// Note: Unwrap html such as it is convertable by `markdownConverter`
|
|
11528
|
+
for (let i = 0; i < 2; i++) {
|
|
11529
|
+
html = html.replace(/<div\s*(?:id="readability-page-\d+"\s+class="page")?>(.*)<\/div>/is, '$1');
|
|
11530
|
+
}
|
|
11531
|
+
if (html.includes('<div')) {
|
|
11532
|
+
html = (article === null || article === void 0 ? void 0 : article.textContent) || '';
|
|
11533
|
+
}
|
|
11534
|
+
const cacheFilehandler = await getScraperIntermediateSource(source, {
|
|
11535
|
+
rootDirname,
|
|
11536
|
+
cacheDirname,
|
|
11537
|
+
intermediateFilesStrategy,
|
|
11538
|
+
extension: 'html',
|
|
11539
|
+
isVerbose,
|
|
11540
|
+
});
|
|
11541
|
+
// Note: Try to cache the scraped content, but don't fail if the filesystem is read-only
|
|
11542
|
+
try {
|
|
11543
|
+
await this.tools.fs.writeFile(cacheFilehandler.filename, html, 'utf-8');
|
|
11544
|
+
}
|
|
11545
|
+
catch (error) {
|
|
11546
|
+
// Note: If we can't write to cache, we'll continue without caching
|
|
11547
|
+
// This handles read-only filesystems like Vercel
|
|
11548
|
+
if (error instanceof Error &&
|
|
11549
|
+
(error.message.includes('EROFS') ||
|
|
11550
|
+
error.message.includes('read-only') ||
|
|
11551
|
+
error.message.includes('EACCES') ||
|
|
11552
|
+
error.message.includes('EPERM') ||
|
|
11553
|
+
error.message.includes('ENOENT'))) ;
|
|
11554
|
+
else {
|
|
11555
|
+
// Re-throw other unexpected errors
|
|
11556
|
+
throw error;
|
|
11557
|
+
}
|
|
11558
|
+
}
|
|
11559
|
+
const markdown = this.showdownConverter.makeMarkdown(html, jsdom.window.document);
|
|
11560
|
+
return { ...cacheFilehandler, markdown };
|
|
11369
11561
|
}
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
const
|
|
11376
|
-
.
|
|
11377
|
-
.
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
|
|
11562
|
+
/**
|
|
11563
|
+
* Scrapes the website and returns the knowledge pieces or `null` if it can't scrape it
|
|
11564
|
+
*/
|
|
11565
|
+
async scrape(source) {
|
|
11566
|
+
const cacheFilehandler = await this.$convert(source);
|
|
11567
|
+
const markdownSource = {
|
|
11568
|
+
source: source.source,
|
|
11569
|
+
filename: cacheFilehandler.filename,
|
|
11570
|
+
url: null,
|
|
11571
|
+
mimeType: 'text/markdown',
|
|
11572
|
+
asText() {
|
|
11573
|
+
return cacheFilehandler.markdown;
|
|
11574
|
+
},
|
|
11575
|
+
asJson() {
|
|
11576
|
+
throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
|
|
11577
|
+
},
|
|
11578
|
+
/*
|
|
11579
|
+
TODO: [🥽]
|
|
11580
|
+
> asBlob() {
|
|
11581
|
+
> throw new UnexpectedError(
|
|
11582
|
+
> 'Did not expect that `markdownScraper` would need to get the content `asBlob`',
|
|
11583
|
+
> );
|
|
11584
|
+
> },
|
|
11585
|
+
*/
|
|
11586
|
+
};
|
|
11587
|
+
const knowledge = this.markdownScraper.scrape(markdownSource);
|
|
11588
|
+
await cacheFilehandler.destroy();
|
|
11589
|
+
return knowledge;
|
|
11390
11590
|
}
|
|
11391
|
-
return spaceTrim$2((block) => `
|
|
11392
|
-
|
|
11393
|
-
${block(usedEnvMessage)}
|
|
11394
|
-
|
|
11395
|
-
Relevant environment variables:
|
|
11396
|
-
${block(Object.keys(env)
|
|
11397
|
-
.filter((envVariableName) => metadata.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)))
|
|
11398
|
-
.map((envVariableName) => `- \`${envVariableName}\``)
|
|
11399
|
-
.join('\n'))}
|
|
11400
|
-
|
|
11401
|
-
Available LLM providers are:
|
|
11402
|
-
${block(metadata
|
|
11403
|
-
.map(({ title, packageName, className, envVariables, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured, }, i) => {
|
|
11404
|
-
const morePieces = [];
|
|
11405
|
-
if (just(false)) ;
|
|
11406
|
-
else if (!isMetadataAviailable && !isInstalled) {
|
|
11407
|
-
// TODO: [�][�] Maybe do allow to do auto-install if package not registered and not found
|
|
11408
|
-
morePieces.push(`Not installed and no metadata, looks like a unexpected behavior`);
|
|
11409
|
-
}
|
|
11410
|
-
else if (isMetadataAviailable && !isInstalled) {
|
|
11411
|
-
// TODO: [�][�]
|
|
11412
|
-
morePieces.push(`Not installed`);
|
|
11413
|
-
}
|
|
11414
|
-
else if (!isMetadataAviailable && isInstalled) {
|
|
11415
|
-
morePieces.push(`No metadata but installed, looks like a unexpected behavior`);
|
|
11416
|
-
}
|
|
11417
|
-
else if (isMetadataAviailable && isInstalled) {
|
|
11418
|
-
morePieces.push(`Installed`);
|
|
11419
|
-
}
|
|
11420
|
-
else {
|
|
11421
|
-
morePieces.push(`unknown state, looks like a unexpected behavior`);
|
|
11422
|
-
} /* not else */
|
|
11423
|
-
if (isFullyConfigured) {
|
|
11424
|
-
morePieces.push(`Configured`);
|
|
11425
|
-
}
|
|
11426
|
-
else if (isPartiallyConfigured) {
|
|
11427
|
-
morePieces.push(`Partially confugured, missing ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined).join(' + ')}`);
|
|
11428
|
-
}
|
|
11429
|
-
else {
|
|
11430
|
-
if (envVariables !== null) {
|
|
11431
|
-
morePieces.push(`Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`);
|
|
11432
|
-
}
|
|
11433
|
-
else {
|
|
11434
|
-
morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
|
|
11435
|
-
}
|
|
11436
|
-
}
|
|
11437
|
-
let providerMessage = spaceTrim$2(`
|
|
11438
|
-
${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
11439
|
-
${morePieces.join('; ')}
|
|
11440
|
-
`);
|
|
11441
|
-
if ($isRunningInNode()) {
|
|
11442
|
-
if (isInstalled && isFullyConfigured) {
|
|
11443
|
-
providerMessage = colors.green(providerMessage);
|
|
11444
|
-
}
|
|
11445
|
-
else if (isInstalled && isPartiallyConfigured) {
|
|
11446
|
-
providerMessage = colors.yellow(providerMessage);
|
|
11447
|
-
}
|
|
11448
|
-
else {
|
|
11449
|
-
providerMessage = colors.gray(providerMessage);
|
|
11450
|
-
}
|
|
11451
|
-
}
|
|
11452
|
-
return providerMessage;
|
|
11453
|
-
})
|
|
11454
|
-
.join('\n'))}
|
|
11455
|
-
`);
|
|
11456
11591
|
}
|
|
11457
11592
|
/**
|
|
11458
|
-
* TODO: [
|
|
11459
|
-
* TODO: [
|
|
11593
|
+
* TODO: [👣] Scraped website in .md can act as cache item - there is no need to run conversion each time
|
|
11594
|
+
* TODO: [🪂] Do it in parallel 11:11
|
|
11595
|
+
* Note: No need to aggregate usage here, it is done by intercepting the llmTools
|
|
11596
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
11460
11597
|
*/
|
|
11461
11598
|
|
|
11462
11599
|
/**
|
|
11463
|
-
*
|
|
11600
|
+
* Fetches and scrapes content from a URL (SERVER-SIDE ONLY)
|
|
11464
11601
|
*
|
|
11465
|
-
*
|
|
11602
|
+
* This function:
|
|
11603
|
+
* 1. Fetches the URL content using promptbookFetch
|
|
11604
|
+
* 2. Determines the content type (HTML, PDF, etc.)
|
|
11605
|
+
* 3. Uses the appropriate scraper to convert to markdown
|
|
11606
|
+
* 4. Returns the scraped markdown content
|
|
11466
11607
|
*
|
|
11467
|
-
* @
|
|
11608
|
+
* @param url The URL to fetch and scrape
|
|
11609
|
+
* @returns Markdown content from the URL
|
|
11610
|
+
*
|
|
11611
|
+
* @private internal utility for USE BROWSER commitment
|
|
11612
|
+
*
|
|
11613
|
+
* WARNING: This function should NOT be used directly in browser environments.
|
|
11614
|
+
* For browser environments, use fetchUrlContentViaBrowser which proxies through
|
|
11615
|
+
* the Agents Server API endpoint at /api/scrape
|
|
11468
11616
|
*/
|
|
11469
|
-
async function
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
'.env.test',
|
|
11476
|
-
'.env.local',
|
|
11477
|
-
'.env.development.local',
|
|
11478
|
-
'.env.development',
|
|
11479
|
-
'.env.production.local',
|
|
11480
|
-
'.env.production',
|
|
11481
|
-
'.env.prod.local',
|
|
11482
|
-
'.env.prod',
|
|
11483
|
-
// <- TODO: Maybe add more patterns
|
|
11484
|
-
];
|
|
11485
|
-
let rootDirname = process.cwd();
|
|
11486
|
-
up_to_root: for (let i = 0; i < LOOP_LIMIT; i++) {
|
|
11487
|
-
for (const pattern of envFilePatterns) {
|
|
11488
|
-
const envFilename = join(rootDirname, pattern);
|
|
11489
|
-
if (await isFileExisting(envFilename, $provideFilesystemForNode())) {
|
|
11490
|
-
$setUsedEnvFilename(envFilename);
|
|
11491
|
-
return envFilename;
|
|
11492
|
-
}
|
|
11617
|
+
async function fetchUrlContent(url) {
|
|
11618
|
+
try {
|
|
11619
|
+
// Validate URL
|
|
11620
|
+
let parsedUrl;
|
|
11621
|
+
try {
|
|
11622
|
+
parsedUrl = new URL(url);
|
|
11493
11623
|
}
|
|
11494
|
-
|
|
11495
|
-
|
|
11624
|
+
catch (error) {
|
|
11625
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
11496
11626
|
}
|
|
11497
|
-
//
|
|
11498
|
-
|
|
11499
|
-
|
|
11500
|
-
|
|
11501
|
-
}
|
|
11502
|
-
|
|
11503
|
-
|
|
11504
|
-
|
|
11627
|
+
// Fetch the URL content
|
|
11628
|
+
const response = await promptbookFetch(url);
|
|
11629
|
+
if (!response.ok) {
|
|
11630
|
+
throw new Error(`Failed to fetch URL: ${response.status} ${response.statusText}`);
|
|
11631
|
+
}
|
|
11632
|
+
// Get content type
|
|
11633
|
+
const contentType = response.headers.get('content-type') || 'text/html';
|
|
11634
|
+
const content = await response.text();
|
|
11635
|
+
// Determine the appropriate scraper based on content type
|
|
11636
|
+
const mimeType = (contentType.split(';')[0] || 'text/html').trim();
|
|
11637
|
+
// Handle PDFs
|
|
11638
|
+
if (mimeType === 'application/pdf' || url.toLowerCase().endsWith('.pdf')) {
|
|
11639
|
+
// TODO: [🔰] Implement PDF scraping using PdfScraper
|
|
11640
|
+
// For now, return a message indicating PDF support is coming
|
|
11641
|
+
return spaceTrim$1(`
|
|
11642
|
+
# PDF Document: ${parsedUrl.pathname.split('/').pop()}
|
|
11643
|
+
|
|
11644
|
+
**Note:** PDF scraping is not yet fully implemented.
|
|
11645
|
+
|
|
11646
|
+
**URL:** ${url}
|
|
11647
|
+
|
|
11648
|
+
The PDF scraper will be integrated to provide full document content extraction.
|
|
11649
|
+
For now, please try to find an HTML version of this content or use a different source.
|
|
11650
|
+
`);
|
|
11651
|
+
}
|
|
11652
|
+
// Handle HTML/websites
|
|
11653
|
+
if (mimeType === 'text/html' || mimeType === 'application/xhtml+xml' || !mimeType.includes('/')) {
|
|
11654
|
+
// Create a scraper source handler
|
|
11655
|
+
const sourceHandler = {
|
|
11656
|
+
source: url,
|
|
11657
|
+
filename: null,
|
|
11658
|
+
url,
|
|
11659
|
+
mimeType: 'text/html',
|
|
11660
|
+
asText: () => content,
|
|
11661
|
+
asJson: () => {
|
|
11662
|
+
throw new Error('JSON conversion not supported for HTML content');
|
|
11663
|
+
},
|
|
11664
|
+
};
|
|
11665
|
+
// Use WebsiteScraper to convert HTML to markdown
|
|
11666
|
+
// Note: We need minimal tools for the scraper
|
|
11667
|
+
const scraper = new WebsiteScraper(// <- TODO: !!!!! Things marked with [🟢] should never be never released in packages that could be imported into browser environment
|
|
11668
|
+
{
|
|
11669
|
+
// Minimal tools - fs and llm are optional for basic scraping
|
|
11670
|
+
fs: undefined,
|
|
11671
|
+
llm: undefined,
|
|
11672
|
+
}, {
|
|
11673
|
+
isVerbose: false,
|
|
11674
|
+
});
|
|
11675
|
+
// Convert to markdown
|
|
11676
|
+
const intermediateSource = await scraper.$convert(sourceHandler);
|
|
11677
|
+
const markdown = intermediateSource.markdown;
|
|
11678
|
+
// Clean up intermediate files
|
|
11679
|
+
await intermediateSource.destroy();
|
|
11680
|
+
// Add URL header to the content
|
|
11681
|
+
return spaceTrim$1(`
|
|
11682
|
+
# Content from: ${url}
|
|
11505
11683
|
|
|
11506
|
-
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
*
|
|
11511
|
-
|
|
11512
|
-
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11684
|
+
${markdown}
|
|
11685
|
+
|
|
11686
|
+
---
|
|
11687
|
+
|
|
11688
|
+
*Source: ${url}*
|
|
11689
|
+
`);
|
|
11690
|
+
}
|
|
11691
|
+
// For other content types, return the raw content with a note
|
|
11692
|
+
return spaceTrim$1(`
|
|
11693
|
+
# Content from: ${url}
|
|
11694
|
+
|
|
11695
|
+
**Content Type:** ${contentType}
|
|
11696
|
+
|
|
11697
|
+
${content}
|
|
11698
|
+
|
|
11699
|
+
---
|
|
11700
|
+
|
|
11701
|
+
*Source: ${url}*
|
|
11702
|
+
`);
|
|
11523
11703
|
}
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11704
|
+
catch (error) {
|
|
11705
|
+
// Handle errors gracefully
|
|
11706
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
11707
|
+
return spaceTrim$1(`
|
|
11708
|
+
# Error fetching content from URL
|
|
11709
|
+
|
|
11710
|
+
**URL:** ${url}
|
|
11711
|
+
|
|
11712
|
+
**Error:** ${errorMessage}
|
|
11713
|
+
|
|
11714
|
+
Unable to fetch and scrape the content from this URL.
|
|
11715
|
+
Please verify the URL is correct and accessible.
|
|
11716
|
+
`);
|
|
11527
11717
|
}
|
|
11528
|
-
const llmToolsConfiguration = $llmToolsMetadataRegister
|
|
11529
|
-
.list()
|
|
11530
|
-
.map((metadata) => metadata.createConfigurationFromEnv(process.env))
|
|
11531
|
-
.filter((configuration) => configuration !== null);
|
|
11532
|
-
return llmToolsConfiguration;
|
|
11533
11718
|
}
|
|
11534
11719
|
/**
|
|
11535
11720
|
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
11536
11721
|
*/
|
|
11537
11722
|
|
|
11538
11723
|
/**
|
|
11539
|
-
*
|
|
11724
|
+
* Generates a regex pattern to match a specific commitment
|
|
11540
11725
|
*
|
|
11541
|
-
* Note:
|
|
11726
|
+
* Note: It always creates new Regex object
|
|
11727
|
+
* Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
|
|
11542
11728
|
*
|
|
11543
|
-
* @
|
|
11729
|
+
* @private - TODO: [🧠] Maybe should be public?
|
|
11544
11730
|
*/
|
|
11545
|
-
function
|
|
11546
|
-
|
|
11547
|
-
|
|
11731
|
+
function createCommitmentRegex(commitment, aliases = [], requiresContent = true) {
|
|
11732
|
+
const allCommitments = [commitment, ...aliases];
|
|
11733
|
+
const patterns = allCommitments.map((commitment) => {
|
|
11734
|
+
const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
11735
|
+
return escapedCommitment.split(/\s+/).join('\\s+');
|
|
11736
|
+
});
|
|
11737
|
+
const keywordPattern = patterns.join('|');
|
|
11738
|
+
if (requiresContent) {
|
|
11739
|
+
return new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
|
|
11548
11740
|
}
|
|
11549
|
-
|
|
11550
|
-
return
|
|
11741
|
+
else {
|
|
11742
|
+
return new RegExp(`^\\s*(?<type>${keywordPattern})\\b(?:\\s+(?<contents>.+))?$`, 'gim');
|
|
11551
11743
|
}
|
|
11552
11744
|
}
|
|
11553
11745
|
/**
|
|
11554
|
-
*
|
|
11555
|
-
*/
|
|
11556
|
-
|
|
11557
|
-
/**
|
|
11558
|
-
* Detects if the code is running in a web worker
|
|
11746
|
+
* Generates a regex pattern to match a specific commitment type
|
|
11559
11747
|
*
|
|
11560
|
-
* Note:
|
|
11748
|
+
* Note: It just matches the type part of the commitment
|
|
11749
|
+
* Note: It always creates new Regex object
|
|
11750
|
+
* Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
|
|
11561
11751
|
*
|
|
11562
|
-
* @
|
|
11752
|
+
* @private
|
|
11563
11753
|
*/
|
|
11564
|
-
function
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
return (
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
}
|
|
11754
|
+
function createCommitmentTypeRegex(commitment, aliases = []) {
|
|
11755
|
+
const allCommitments = [commitment, ...aliases];
|
|
11756
|
+
const patterns = allCommitments.map((commitment) => {
|
|
11757
|
+
const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
11758
|
+
return escapedCommitment.split(/\s+/).join('\\s+');
|
|
11759
|
+
});
|
|
11760
|
+
const keywordPattern = patterns.join('|');
|
|
11761
|
+
const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b`, 'gim');
|
|
11762
|
+
return regex;
|
|
11574
11763
|
}
|
|
11575
|
-
/**
|
|
11576
|
-
* TODO: [🎺]
|
|
11577
|
-
*/
|
|
11578
11764
|
|
|
11579
11765
|
/**
|
|
11580
|
-
*
|
|
11581
|
-
*
|
|
11582
|
-
* Instantiates and configures LLM tool instances for each configuration entry,
|
|
11583
|
-
* combining them into a unified interface via MultipleLlmExecutionTools.
|
|
11584
|
-
*
|
|
11585
|
-
* Note: This function is not cached, every call creates new instance of `MultipleLlmExecutionTools`
|
|
11766
|
+
* Base implementation of CommitmentDefinition that provides common functionality
|
|
11767
|
+
* Most commitments can extend this class and only override the applyToAgentModelRequirements method
|
|
11586
11768
|
*
|
|
11587
|
-
* @
|
|
11588
|
-
* @param options Additional options for configuring the LLM tools
|
|
11589
|
-
* @returns A unified interface combining all successfully instantiated LLM tools
|
|
11590
|
-
* @public exported from `@promptbook/core`
|
|
11769
|
+
* @private
|
|
11591
11770
|
*/
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
11598
|
-
|
|
11599
|
-
|
|
11600
|
-
|
|
11601
|
-
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11771
|
+
class BaseCommitmentDefinition {
|
|
11772
|
+
constructor(type, aliases = []) {
|
|
11773
|
+
this.type = type;
|
|
11774
|
+
this.aliases = aliases;
|
|
11775
|
+
}
|
|
11776
|
+
/**
|
|
11777
|
+
* Whether this commitment requires content.
|
|
11778
|
+
* If true, regex will match only if there is content after the commitment keyword.
|
|
11779
|
+
* If false, regex will match even if there is no content.
|
|
11780
|
+
*/
|
|
11781
|
+
get requiresContent() {
|
|
11782
|
+
return true;
|
|
11783
|
+
}
|
|
11784
|
+
/**
|
|
11785
|
+
* Creates a regex pattern to match this commitment in agent source
|
|
11786
|
+
* Uses the existing createCommitmentRegex function as internal helper
|
|
11787
|
+
*/
|
|
11788
|
+
createRegex() {
|
|
11789
|
+
return createCommitmentRegex(this.type, this.aliases, this.requiresContent);
|
|
11790
|
+
}
|
|
11791
|
+
/**
|
|
11792
|
+
* Creates a regex pattern to match just the commitment type
|
|
11793
|
+
* Uses the existing createCommitmentTypeRegex function as internal helper
|
|
11794
|
+
*/
|
|
11795
|
+
createTypeRegex() {
|
|
11796
|
+
return createCommitmentTypeRegex(this.type, this.aliases);
|
|
11797
|
+
}
|
|
11798
|
+
/**
|
|
11799
|
+
* Helper method to create a new requirements object with updated system message
|
|
11800
|
+
* This is commonly used by many commitments
|
|
11801
|
+
*/
|
|
11802
|
+
updateSystemMessage(requirements, messageUpdate) {
|
|
11803
|
+
const newMessage = typeof messageUpdate === 'string' ? messageUpdate : messageUpdate(requirements.systemMessage);
|
|
11804
|
+
return {
|
|
11805
|
+
...requirements,
|
|
11806
|
+
systemMessage: newMessage,
|
|
11807
|
+
};
|
|
11808
|
+
}
|
|
11809
|
+
/**
|
|
11810
|
+
* Helper method to append content to the system message
|
|
11811
|
+
*/
|
|
11812
|
+
appendToSystemMessage(requirements, content, separator = '\n\n') {
|
|
11813
|
+
return this.updateSystemMessage(requirements, (currentMessage) => {
|
|
11814
|
+
if (!currentMessage.trim()) {
|
|
11815
|
+
return content;
|
|
11816
|
+
}
|
|
11817
|
+
return currentMessage + separator + content;
|
|
11623
11818
|
});
|
|
11624
|
-
}
|
|
11625
|
-
|
|
11819
|
+
}
|
|
11820
|
+
/**
|
|
11821
|
+
* Helper method to add a comment section to the system message
|
|
11822
|
+
* Comments are lines starting with # that will be removed from the final system message
|
|
11823
|
+
* but can be useful for organizing and structuring the message during processing
|
|
11824
|
+
*/
|
|
11825
|
+
addCommentSection(requirements, commentTitle, content, position = 'end') {
|
|
11826
|
+
const commentSection = `# ${commentTitle.toUpperCase()}\n${content}`;
|
|
11827
|
+
if (position === 'beginning') {
|
|
11828
|
+
return this.updateSystemMessage(requirements, (currentMessage) => {
|
|
11829
|
+
if (!currentMessage.trim()) {
|
|
11830
|
+
return commentSection;
|
|
11831
|
+
}
|
|
11832
|
+
return commentSection + '\n\n' + currentMessage;
|
|
11833
|
+
});
|
|
11834
|
+
}
|
|
11835
|
+
else {
|
|
11836
|
+
return this.appendToSystemMessage(requirements, commentSection);
|
|
11837
|
+
}
|
|
11838
|
+
}
|
|
11839
|
+
/**
|
|
11840
|
+
* Gets tool function implementations provided by this commitment
|
|
11841
|
+
*
|
|
11842
|
+
* When the `applyToAgentModelRequirements` adds tools to the requirements, this method should return the corresponding function definitions.
|
|
11843
|
+
*/
|
|
11844
|
+
getToolFunctions() {
|
|
11845
|
+
return {};
|
|
11846
|
+
}
|
|
11847
|
+
/**
|
|
11848
|
+
* Gets human-readable titles for tool functions provided by this commitment
|
|
11849
|
+
*
|
|
11850
|
+
* This is used in the UI to show a user-friendly name instead of the technical function name.
|
|
11851
|
+
*/
|
|
11852
|
+
getToolTitles() {
|
|
11853
|
+
return {};
|
|
11854
|
+
}
|
|
11626
11855
|
}
|
|
11627
|
-
/**
|
|
11628
|
-
* TODO: [🎌] Together with `createLlmToolsFromConfiguration` + 'EXECUTION_TOOLS_CLASSES' gets to `@promptbook/core` ALL model providers, make this more efficient
|
|
11629
|
-
* TODO: [🧠][🎌] Dynamically install required providers
|
|
11630
|
-
* TODO: We should implement an interactive configuration wizard that would:
|
|
11631
|
-
* 1. Detect which LLM providers are available in the environment
|
|
11632
|
-
* 2. Guide users through required configuration settings for each provider
|
|
11633
|
-
* 3. Allow testing connections before completing setup
|
|
11634
|
-
* 4. Generate appropriate configuration code for application integration
|
|
11635
|
-
* TODO: [🧠][🍛] Which name is better `createLlmToolsFromConfig` or `createLlmToolsFromConfiguration`?
|
|
11636
|
-
* TODO: [🧠] Is there some meaningfull way how to test this util
|
|
11637
|
-
* TODO: This should be maybe not under `_common` but under `utils`
|
|
11638
|
-
* TODO: [®] DRY Register logic
|
|
11639
|
-
*/
|
|
11640
11856
|
|
|
11641
11857
|
/**
|
|
11642
|
-
*
|
|
11643
|
-
*
|
|
11644
|
-
* This utility function detects available LLM providers based on environment variables
|
|
11645
|
-
* and creates properly configured LLM execution tools for each detected provider.
|
|
11858
|
+
* ACTION commitment definition
|
|
11646
11859
|
*
|
|
11647
|
-
*
|
|
11860
|
+
* The ACTION commitment defines specific actions or capabilities that the agent can perform.
|
|
11861
|
+
* This helps define what the agent is capable of doing and how it should approach tasks.
|
|
11648
11862
|
*
|
|
11649
|
-
*
|
|
11650
|
-
* Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
|
|
11863
|
+
* Example usage in agent source:
|
|
11651
11864
|
*
|
|
11652
|
-
*
|
|
11653
|
-
*
|
|
11654
|
-
*
|
|
11655
|
-
*
|
|
11865
|
+
* ```book
|
|
11866
|
+
* ACTION Can generate code snippets and explain programming concepts
|
|
11867
|
+
* ACTION Able to analyze data and provide insights
|
|
11868
|
+
* ```
|
|
11656
11869
|
*
|
|
11657
|
-
* @
|
|
11658
|
-
* @returns A unified interface containing all detected and configured LLM tools
|
|
11659
|
-
* @public exported from `@promptbook/node`
|
|
11870
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
11660
11871
|
*/
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
|
|
11872
|
+
class ActionCommitmentDefinition extends BaseCommitmentDefinition {
|
|
11873
|
+
constructor(type = 'ACTION') {
|
|
11874
|
+
super(type);
|
|
11664
11875
|
}
|
|
11665
|
-
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
|
|
11876
|
+
/**
|
|
11877
|
+
* Short one-line description of ACTION.
|
|
11878
|
+
*/
|
|
11879
|
+
get description() {
|
|
11880
|
+
return 'Define agent capabilities and actions it can perform.';
|
|
11881
|
+
}
|
|
11882
|
+
/**
|
|
11883
|
+
* Icon for this commitment.
|
|
11884
|
+
*/
|
|
11885
|
+
get icon() {
|
|
11886
|
+
return '⚡';
|
|
11887
|
+
}
|
|
11888
|
+
/**
|
|
11889
|
+
* Markdown documentation for ACTION commitment.
|
|
11890
|
+
*/
|
|
11891
|
+
get documentation() {
|
|
11892
|
+
return spaceTrim$1(`
|
|
11893
|
+
# ${this.type}
|
|
11670
11894
|
|
|
11671
|
-
|
|
11672
|
-
`));
|
|
11673
|
-
}
|
|
11674
|
-
// TODO: [🥃]
|
|
11675
|
-
throw new Error(spaceTrim$2((block) => `
|
|
11676
|
-
No LLM tools found in the environment
|
|
11895
|
+
Defines specific actions or capabilities that the agent can perform.
|
|
11677
11896
|
|
|
11678
|
-
|
|
11679
|
-
`));
|
|
11680
|
-
}
|
|
11681
|
-
return createLlmToolsFromConfiguration(configuration, options);
|
|
11682
|
-
}
|
|
11683
|
-
/**
|
|
11684
|
-
* TODO: The architecture for LLM tools configuration consists of three key functions:
|
|
11685
|
-
* 1. `$provideLlmToolsFromEnv` - High-level function that detects available providers from env vars and returns ready-to-use LLM tools
|
|
11686
|
-
* 2. `$provideLlmToolsConfigurationFromEnv` - Middle layer that extracts configuration objects from environment variables
|
|
11687
|
-
* 3. `createLlmToolsFromConfiguration` - Low-level function that instantiates LLM tools from explicit configuration
|
|
11688
|
-
*
|
|
11689
|
-
* This layered approach allows flexibility in how tools are configured:
|
|
11690
|
-
* - Use $provideLlmToolsFromEnv for automatic detection and setup in Node.js environments
|
|
11691
|
-
* - Use $provideLlmToolsConfigurationFromEnv to extract config objects for modification before instantiation
|
|
11692
|
-
* - Use createLlmToolsFromConfiguration for explicit control over tool configurations
|
|
11693
|
-
*
|
|
11694
|
-
* TODO: [🧠][🍛] Which name is better `$provideLlmToolsFromEnv` or `$provideLlmToolsFromEnvironment`?
|
|
11695
|
-
* TODO: [🧠] Is there some meaningfull way how to test this util
|
|
11696
|
-
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
11697
|
-
* TODO: [🥃] Allow `ptbk make` without llm tools
|
|
11698
|
-
* TODO: This should be maybe not under `_common` but under `utils`
|
|
11699
|
-
* TODO: [®] DRY Register logic
|
|
11700
|
-
*/
|
|
11897
|
+
## Key aspects
|
|
11701
11898
|
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11899
|
+
- Both terms work identically and can be used interchangeably.
|
|
11900
|
+
- Each action adds to the agent's capability list.
|
|
11901
|
+
- Actions help users understand what the agent can do.
|
|
11902
|
+
|
|
11903
|
+
## Examples
|
|
11904
|
+
|
|
11905
|
+
\`\`\`book
|
|
11906
|
+
Code Assistant
|
|
11907
|
+
|
|
11908
|
+
PERSONA You are a programming assistant
|
|
11909
|
+
ACTION Can generate code snippets and explain programming concepts
|
|
11910
|
+
ACTION Able to debug existing code and suggest improvements
|
|
11911
|
+
ACTION Can create unit tests for functions
|
|
11912
|
+
\`\`\`
|
|
11913
|
+
|
|
11914
|
+
\`\`\`book
|
|
11915
|
+
Data Scientist
|
|
11916
|
+
|
|
11917
|
+
PERSONA You are a data analysis expert
|
|
11918
|
+
ACTION Able to analyze data and provide insights
|
|
11919
|
+
ACTION Can create visualizations and charts
|
|
11920
|
+
ACTION Capable of statistical analysis and modeling
|
|
11921
|
+
KNOWLEDGE Data analysis best practices and statistical methods
|
|
11922
|
+
\`\`\`
|
|
11923
|
+
`);
|
|
11711
11924
|
}
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
const scraper = await scraperFactory(tools, options || {});
|
|
11717
|
-
if (scraper.metadata.packageName === '@promptbook/boilerplate' ||
|
|
11718
|
-
scraper.metadata.mimeTypes.some((mimeType) => mimeType.includes('DISABLED'))) {
|
|
11719
|
-
continue;
|
|
11925
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
11926
|
+
const trimmedContent = content.trim();
|
|
11927
|
+
if (!trimmedContent) {
|
|
11928
|
+
return requirements;
|
|
11720
11929
|
}
|
|
11721
|
-
|
|
11930
|
+
// Add action capability to the system message
|
|
11931
|
+
const actionSection = `Capability: ${trimmedContent}`;
|
|
11932
|
+
return this.appendToSystemMessage(requirements, actionSection, '\n\n');
|
|
11722
11933
|
}
|
|
11723
|
-
return scrapers;
|
|
11724
11934
|
}
|
|
11725
11935
|
/**
|
|
11726
|
-
* Note: [
|
|
11936
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
11727
11937
|
*/
|
|
11728
11938
|
|
|
11729
11939
|
/**
|
|
11730
|
-
*
|
|
11940
|
+
* CLOSED commitment definition
|
|
11731
11941
|
*
|
|
11732
|
-
*
|
|
11733
|
-
|
|
11734
|
-
function computeHash(value) {
|
|
11735
|
-
return SHA256(hexEncoder.parse(spaceTrim$2(valueToString(value)))).toString( /* hex */);
|
|
11736
|
-
}
|
|
11737
|
-
/**
|
|
11738
|
-
* TODO: [🥬][🥬] Use this ACRY
|
|
11739
|
-
*/
|
|
11740
|
-
|
|
11741
|
-
/**
|
|
11742
|
-
* Makes first letter of a string lowercase
|
|
11942
|
+
* The CLOSED commitment specifies that the agent CANNOT be modified by conversation.
|
|
11943
|
+
* It prevents the agent from learning from interactions and updating its source code.
|
|
11743
11944
|
*
|
|
11744
|
-
*
|
|
11945
|
+
* Example usage in agent source:
|
|
11745
11946
|
*
|
|
11746
|
-
*
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
return word.substring(0, 1).toLowerCase() + word.substring(1);
|
|
11750
|
-
}
|
|
11751
|
-
|
|
11752
|
-
/**
|
|
11753
|
-
* Parses keywords from a string
|
|
11947
|
+
* ```book
|
|
11948
|
+
* CLOSED
|
|
11949
|
+
* ```
|
|
11754
11950
|
*
|
|
11755
|
-
* @
|
|
11756
|
-
* @returns {Set} of keywords without diacritics in lowercase
|
|
11757
|
-
* @public exported from `@promptbook/utils`
|
|
11951
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
11758
11952
|
*/
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11953
|
+
class ClosedCommitmentDefinition extends BaseCommitmentDefinition {
|
|
11954
|
+
constructor() {
|
|
11955
|
+
super('CLOSED');
|
|
11956
|
+
}
|
|
11957
|
+
/**
|
|
11958
|
+
* The `CLOSED` commitment is standalone.
|
|
11959
|
+
*/
|
|
11960
|
+
get requiresContent() {
|
|
11961
|
+
return false;
|
|
11962
|
+
}
|
|
11963
|
+
/**
|
|
11964
|
+
* Short one-line description of CLOSED.
|
|
11965
|
+
*/
|
|
11966
|
+
get description() {
|
|
11967
|
+
return 'Prevent the agent from being modified by conversation.';
|
|
11968
|
+
}
|
|
11969
|
+
/**
|
|
11970
|
+
* Icon for this commitment.
|
|
11971
|
+
*/
|
|
11972
|
+
get icon() {
|
|
11973
|
+
return '🔒';
|
|
11974
|
+
}
|
|
11975
|
+
/**
|
|
11976
|
+
* Markdown documentation for CLOSED commitment.
|
|
11977
|
+
*/
|
|
11978
|
+
get documentation() {
|
|
11979
|
+
return spaceTrim$1(`
|
|
11980
|
+
# CLOSED
|
|
11766
11981
|
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
*
|
|
11770
|
-
* @param name The string to be converted to a URI-compatible format.
|
|
11771
|
-
* @returns A URI-compatible string derived from the input name.
|
|
11772
|
-
* @example 'Hello World' -> 'hello-world'
|
|
11773
|
-
* @public exported from `@promptbook/utils`
|
|
11774
|
-
*/
|
|
11775
|
-
function nameToUriPart(name) {
|
|
11776
|
-
let uriPart = name;
|
|
11777
|
-
uriPart = uriPart.toLowerCase();
|
|
11778
|
-
uriPart = removeDiacritics(uriPart);
|
|
11779
|
-
uriPart = uriPart.replace(/[^a-zA-Z0-9]+/g, '-');
|
|
11780
|
-
uriPart = uriPart.replace(/^-+/, '');
|
|
11781
|
-
uriPart = uriPart.replace(/-+$/, '');
|
|
11782
|
-
return uriPart;
|
|
11783
|
-
}
|
|
11982
|
+
Specifies that the agent **cannot** be modified by conversation with it.
|
|
11983
|
+
This means the agent will **not** learn from interactions and its source code will remain static during conversation.
|
|
11784
11984
|
|
|
11785
|
-
|
|
11786
|
-
* Converts a given name into URI-compatible parts.
|
|
11787
|
-
*
|
|
11788
|
-
* @param name The name to be converted into URI parts.
|
|
11789
|
-
* @returns An array of URI-compatible parts derived from the name.
|
|
11790
|
-
* @example 'Example Name' -> ['example', 'name']
|
|
11791
|
-
* @public exported from `@promptbook/utils`
|
|
11792
|
-
*/
|
|
11793
|
-
function nameToUriParts(name) {
|
|
11794
|
-
return nameToUriPart(name)
|
|
11795
|
-
.split('-')
|
|
11796
|
-
.filter((value) => value !== '');
|
|
11797
|
-
}
|
|
11985
|
+
By default (if not specified), agents are \`OPEN\` to modification.
|
|
11798
11986
|
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
* Note: [🔂] This function is idempotent.
|
|
11803
|
-
*
|
|
11804
|
-
* @param text @public exported from `@promptbook/utils`
|
|
11805
|
-
* @returns
|
|
11806
|
-
* @example 'HelloWorld'
|
|
11807
|
-
* @example 'ILovePromptbook'
|
|
11808
|
-
* @public exported from `@promptbook/utils`
|
|
11809
|
-
*/
|
|
11810
|
-
function normalizeTo_PascalCase(text) {
|
|
11811
|
-
return normalizeTo_camelCase(text, true);
|
|
11812
|
-
}
|
|
11987
|
+
> See also [OPEN](/docs/OPEN)
|
|
11988
|
+
|
|
11989
|
+
## Example
|
|
11813
11990
|
|
|
11991
|
+
\`\`\`book
|
|
11992
|
+
CLOSED
|
|
11993
|
+
\`\`\`
|
|
11994
|
+
`);
|
|
11995
|
+
}
|
|
11996
|
+
applyToAgentModelRequirements(requirements, _content) {
|
|
11997
|
+
const updatedMetadata = {
|
|
11998
|
+
...requirements.metadata,
|
|
11999
|
+
isClosed: true,
|
|
12000
|
+
};
|
|
12001
|
+
return {
|
|
12002
|
+
...requirements,
|
|
12003
|
+
metadata: updatedMetadata,
|
|
12004
|
+
};
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
11814
12007
|
/**
|
|
11815
|
-
*
|
|
11816
|
-
*
|
|
11817
|
-
* Note: [🔂] This function is idempotent.
|
|
11818
|
-
*
|
|
11819
|
-
* @public exported from `@promptbook/utils`
|
|
12008
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
11820
12009
|
*/
|
|
11821
|
-
function normalizeWhitespaces(sentence) {
|
|
11822
|
-
return sentence.replace(/\s+/gs, ' ').trim();
|
|
11823
|
-
}
|
|
11824
12010
|
|
|
11825
12011
|
/**
|
|
11826
|
-
*
|
|
12012
|
+
* COMPONENT commitment definition
|
|
11827
12013
|
*
|
|
11828
|
-
*
|
|
11829
|
-
* Note: This function trims the text and removes whole introduce sentence if it is present
|
|
11830
|
-
* Note: There are two similar functions:
|
|
11831
|
-
* - `removeQuotes` which removes only bounding quotes
|
|
11832
|
-
* - `unwrapResult` which removes whole introduce sentence
|
|
12014
|
+
* The COMPONENT commitment defines a UI component that the agent can render in the chat.
|
|
11833
12015
|
*
|
|
11834
|
-
* @
|
|
11835
|
-
* @returns text without quotes
|
|
11836
|
-
* @public exported from `@promptbook/utils`
|
|
12016
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
11837
12017
|
*/
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
// Remove leading and trailing spaces and newlines
|
|
11842
|
-
if (isTrimmed) {
|
|
11843
|
-
trimmedText = spaceTrim$1(trimmedText);
|
|
11844
|
-
}
|
|
11845
|
-
let processedText = trimmedText;
|
|
11846
|
-
// Check for markdown code block
|
|
11847
|
-
const codeBlockRegex = /^```[a-z]*\n([\s\S]*?)\n```\s*$/;
|
|
11848
|
-
const codeBlockMatch = processedText.match(codeBlockRegex);
|
|
11849
|
-
if (codeBlockMatch && codeBlockMatch[1] !== undefined) {
|
|
11850
|
-
// Check if there's only one code block
|
|
11851
|
-
const codeBlockCount = (processedText.match(/```/g) || []).length / 2;
|
|
11852
|
-
if (codeBlockCount === 1) {
|
|
11853
|
-
return unwrapResult(codeBlockMatch[1], { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
11854
|
-
}
|
|
12018
|
+
class ComponentCommitmentDefinition extends BaseCommitmentDefinition {
|
|
12019
|
+
constructor() {
|
|
12020
|
+
super('COMPONENT');
|
|
11855
12021
|
}
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
}
|
|
11862
|
-
processedText = spaceTrim$1(processedText);
|
|
11863
|
-
// Check again for code block after removing introduce sentence
|
|
11864
|
-
const codeBlockMatch2 = processedText.match(codeBlockRegex);
|
|
11865
|
-
if (codeBlockMatch2 && codeBlockMatch2[1] !== undefined) {
|
|
11866
|
-
const codeBlockCount = (processedText.match(/```/g) || []).length / 2;
|
|
11867
|
-
if (codeBlockCount === 1) {
|
|
11868
|
-
return unwrapResult(codeBlockMatch2[1], { isTrimmed: false, isIntroduceSentenceRemoved: false });
|
|
11869
|
-
}
|
|
11870
|
-
}
|
|
12022
|
+
/**
|
|
12023
|
+
* Short one-line description of COMPONENT.
|
|
12024
|
+
*/
|
|
12025
|
+
get description() {
|
|
12026
|
+
return 'Define a UI component that the agent can render in the chat.';
|
|
11871
12027
|
}
|
|
11872
|
-
|
|
11873
|
-
|
|
12028
|
+
/**
|
|
12029
|
+
* Icon for this commitment.
|
|
12030
|
+
*/
|
|
12031
|
+
get icon() {
|
|
12032
|
+
return '🧩';
|
|
11874
12033
|
}
|
|
11875
|
-
|
|
11876
|
-
|
|
12034
|
+
/**
|
|
12035
|
+
* Markdown documentation for COMPONENT commitment.
|
|
12036
|
+
*/
|
|
12037
|
+
get documentation() {
|
|
12038
|
+
return spaceTrim$1(`
|
|
12039
|
+
# COMPONENT
|
|
12040
|
+
|
|
12041
|
+
Defines a UI component that the agent can render in the chat.
|
|
12042
|
+
|
|
12043
|
+
## Key aspects
|
|
12044
|
+
|
|
12045
|
+
- Tells the agent that a specific component is available.
|
|
12046
|
+
- Provides syntax for using the component.
|
|
12047
|
+
|
|
12048
|
+
## Example
|
|
12049
|
+
|
|
12050
|
+
\`\`\`book
|
|
12051
|
+
COMPONENT Arrow
|
|
12052
|
+
The agent should render an arrow component in the chat UI.
|
|
12053
|
+
Syntax:
|
|
12054
|
+
<Arrow direction="up" color="red" />
|
|
12055
|
+
\`\`\`
|
|
12056
|
+
`);
|
|
11877
12057
|
}
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
['"', '"'],
|
|
11883
|
-
["'", "'"],
|
|
11884
|
-
['`', '`'],
|
|
11885
|
-
['*', '*'],
|
|
11886
|
-
['_', '_'],
|
|
11887
|
-
['„', '“'],
|
|
11888
|
-
['«', '»'] /* <- QUOTES to config */,
|
|
11889
|
-
].some(([startQuote, endQuote]) => {
|
|
11890
|
-
if (!processedText.startsWith(startQuote)) {
|
|
11891
|
-
return false;
|
|
11892
|
-
}
|
|
11893
|
-
if (!processedText.endsWith(endQuote)) {
|
|
11894
|
-
return false;
|
|
11895
|
-
}
|
|
11896
|
-
if (unquotedText.includes(startQuote) && !unquotedText.includes(endQuote)) {
|
|
11897
|
-
return false;
|
|
11898
|
-
}
|
|
11899
|
-
if (!unquotedText.includes(startQuote) && unquotedText.includes(endQuote)) {
|
|
11900
|
-
return false;
|
|
12058
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
12059
|
+
const trimmedContent = content.trim();
|
|
12060
|
+
if (!trimmedContent) {
|
|
12061
|
+
return requirements;
|
|
11901
12062
|
}
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
return
|
|
11905
|
-
}
|
|
11906
|
-
else {
|
|
11907
|
-
return processedText;
|
|
12063
|
+
// Add component capability to the system message
|
|
12064
|
+
const componentSection = `Component: ${trimmedContent}`;
|
|
12065
|
+
return this.appendToSystemMessage(requirements, componentSection, '\n\n');
|
|
11908
12066
|
}
|
|
11909
12067
|
}
|
|
11910
12068
|
/**
|
|
11911
|
-
*
|
|
12069
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
11912
12070
|
*/
|
|
11913
12071
|
|
|
11914
|
-
// <- TODO: Auto convert to type `import { ... } from 'type-fest';`
|
|
11915
12072
|
/**
|
|
11916
|
-
*
|
|
12073
|
+
* DELETE commitment definition
|
|
11917
12074
|
*
|
|
11918
|
-
*
|
|
11919
|
-
*
|
|
11920
|
-
*
|
|
11921
|
-
* - Objects and arrays are serializable if all their properties are serializable
|
|
11922
|
-
* - Functions are not serializable
|
|
11923
|
-
* - Circular references are not serializable
|
|
11924
|
-
* - `Date` objects are not serializable
|
|
11925
|
-
* - `Map` and `Set` objects are not serializable
|
|
11926
|
-
* - `RegExp` objects are not serializable
|
|
11927
|
-
* - `Error` objects are not serializable
|
|
11928
|
-
* - `Symbol` objects are not serializable
|
|
11929
|
-
* - And much more...
|
|
11930
|
-
*
|
|
11931
|
-
*
|
|
11932
|
-
* @public exported from `@promptbook/utils`
|
|
11933
|
-
*/
|
|
11934
|
-
function isSerializableAsJson(value) {
|
|
11935
|
-
try {
|
|
11936
|
-
checkSerializableAsJson({ value });
|
|
11937
|
-
return true;
|
|
11938
|
-
}
|
|
11939
|
-
catch (error) {
|
|
11940
|
-
return false;
|
|
11941
|
-
}
|
|
11942
|
-
}
|
|
11943
|
-
/**
|
|
11944
|
-
* TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
|
|
11945
|
-
* TODO: [🧠][💺] Can be done this on type-level?
|
|
11946
|
-
*/
|
|
11947
|
-
|
|
11948
|
-
/**
|
|
11949
|
-
* Tests if given string is valid agent URL
|
|
11950
|
-
*
|
|
11951
|
-
* Note: There are few similar functions:
|
|
11952
|
-
* - `isValidUrl` which tests any URL
|
|
11953
|
-
* - `isValidAgentUrl` *(this one)* which tests just agent URL
|
|
11954
|
-
* - `isValidPipelineUrl` which tests just pipeline URL
|
|
11955
|
-
*
|
|
11956
|
-
* @public exported from `@promptbook/utils`
|
|
11957
|
-
*/
|
|
11958
|
-
function isValidAgentUrl(url) {
|
|
11959
|
-
if (!isValidUrl(url)) {
|
|
11960
|
-
return false;
|
|
11961
|
-
}
|
|
11962
|
-
if (!url.startsWith('https://') && !url.startsWith('http://') /* <- Note: [👣] */) {
|
|
11963
|
-
return false;
|
|
11964
|
-
}
|
|
11965
|
-
if (url.includes('#')) {
|
|
11966
|
-
// TODO: [🐠]
|
|
11967
|
-
return false;
|
|
11968
|
-
}
|
|
11969
|
-
/*
|
|
11970
|
-
Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
|
|
11971
|
-
if (isUrlOnPrivateNetwork(url)) {
|
|
11972
|
-
return false;
|
|
11973
|
-
}
|
|
11974
|
-
*/
|
|
11975
|
-
return true;
|
|
11976
|
-
}
|
|
11977
|
-
/**
|
|
11978
|
-
* TODO: [🐠] Maybe more info why the URL is invalid
|
|
11979
|
-
*/
|
|
11980
|
-
|
|
11981
|
-
/**
|
|
11982
|
-
* Generates a regex pattern to match a specific commitment
|
|
11983
|
-
*
|
|
11984
|
-
* Note: It always creates new Regex object
|
|
11985
|
-
* Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
|
|
11986
|
-
*
|
|
11987
|
-
* @private - TODO: [🧠] Maybe should be public?
|
|
11988
|
-
*/
|
|
11989
|
-
function createCommitmentRegex(commitment, aliases = [], requiresContent = true) {
|
|
11990
|
-
const allCommitments = [commitment, ...aliases];
|
|
11991
|
-
const patterns = allCommitments.map((commitment) => {
|
|
11992
|
-
const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
11993
|
-
return escapedCommitment.split(/\s+/).join('\\s+');
|
|
11994
|
-
});
|
|
11995
|
-
const keywordPattern = patterns.join('|');
|
|
11996
|
-
if (requiresContent) {
|
|
11997
|
-
return new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
|
|
11998
|
-
}
|
|
11999
|
-
else {
|
|
12000
|
-
return new RegExp(`^\\s*(?<type>${keywordPattern})\\b(?:\\s+(?<contents>.+))?$`, 'gim');
|
|
12001
|
-
}
|
|
12002
|
-
}
|
|
12003
|
-
/**
|
|
12004
|
-
* Generates a regex pattern to match a specific commitment type
|
|
12005
|
-
*
|
|
12006
|
-
* Note: It just matches the type part of the commitment
|
|
12007
|
-
* Note: It always creates new Regex object
|
|
12008
|
-
* Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
|
|
12009
|
-
*
|
|
12010
|
-
* @private
|
|
12011
|
-
*/
|
|
12012
|
-
function createCommitmentTypeRegex(commitment, aliases = []) {
|
|
12013
|
-
const allCommitments = [commitment, ...aliases];
|
|
12014
|
-
const patterns = allCommitments.map((commitment) => {
|
|
12015
|
-
const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
12016
|
-
return escapedCommitment.split(/\s+/).join('\\s+');
|
|
12017
|
-
});
|
|
12018
|
-
const keywordPattern = patterns.join('|');
|
|
12019
|
-
const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b`, 'gim');
|
|
12020
|
-
return regex;
|
|
12021
|
-
}
|
|
12022
|
-
|
|
12023
|
-
/**
|
|
12024
|
-
* Base implementation of CommitmentDefinition that provides common functionality
|
|
12025
|
-
* Most commitments can extend this class and only override the applyToAgentModelRequirements method
|
|
12026
|
-
*
|
|
12027
|
-
* @private
|
|
12028
|
-
*/
|
|
12029
|
-
class BaseCommitmentDefinition {
|
|
12030
|
-
constructor(type, aliases = []) {
|
|
12031
|
-
this.type = type;
|
|
12032
|
-
this.aliases = aliases;
|
|
12033
|
-
}
|
|
12034
|
-
/**
|
|
12035
|
-
* Whether this commitment requires content.
|
|
12036
|
-
* If true, regex will match only if there is content after the commitment keyword.
|
|
12037
|
-
* If false, regex will match even if there is no content.
|
|
12038
|
-
*/
|
|
12039
|
-
get requiresContent() {
|
|
12040
|
-
return true;
|
|
12041
|
-
}
|
|
12042
|
-
/**
|
|
12043
|
-
* Creates a regex pattern to match this commitment in agent source
|
|
12044
|
-
* Uses the existing createCommitmentRegex function as internal helper
|
|
12045
|
-
*/
|
|
12046
|
-
createRegex() {
|
|
12047
|
-
return createCommitmentRegex(this.type, this.aliases, this.requiresContent);
|
|
12048
|
-
}
|
|
12049
|
-
/**
|
|
12050
|
-
* Creates a regex pattern to match just the commitment type
|
|
12051
|
-
* Uses the existing createCommitmentTypeRegex function as internal helper
|
|
12052
|
-
*/
|
|
12053
|
-
createTypeRegex() {
|
|
12054
|
-
return createCommitmentTypeRegex(this.type, this.aliases);
|
|
12055
|
-
}
|
|
12056
|
-
/**
|
|
12057
|
-
* Helper method to create a new requirements object with updated system message
|
|
12058
|
-
* This is commonly used by many commitments
|
|
12059
|
-
*/
|
|
12060
|
-
updateSystemMessage(requirements, messageUpdate) {
|
|
12061
|
-
const newMessage = typeof messageUpdate === 'string' ? messageUpdate : messageUpdate(requirements.systemMessage);
|
|
12062
|
-
return {
|
|
12063
|
-
...requirements,
|
|
12064
|
-
systemMessage: newMessage,
|
|
12065
|
-
};
|
|
12066
|
-
}
|
|
12067
|
-
/**
|
|
12068
|
-
* Helper method to append content to the system message
|
|
12069
|
-
*/
|
|
12070
|
-
appendToSystemMessage(requirements, content, separator = '\n\n') {
|
|
12071
|
-
return this.updateSystemMessage(requirements, (currentMessage) => {
|
|
12072
|
-
if (!currentMessage.trim()) {
|
|
12073
|
-
return content;
|
|
12074
|
-
}
|
|
12075
|
-
return currentMessage + separator + content;
|
|
12076
|
-
});
|
|
12077
|
-
}
|
|
12078
|
-
/**
|
|
12079
|
-
* Helper method to add a comment section to the system message
|
|
12080
|
-
* Comments are lines starting with # that will be removed from the final system message
|
|
12081
|
-
* but can be useful for organizing and structuring the message during processing
|
|
12082
|
-
*/
|
|
12083
|
-
addCommentSection(requirements, commentTitle, content, position = 'end') {
|
|
12084
|
-
const commentSection = `# ${commentTitle.toUpperCase()}\n${content}`;
|
|
12085
|
-
if (position === 'beginning') {
|
|
12086
|
-
return this.updateSystemMessage(requirements, (currentMessage) => {
|
|
12087
|
-
if (!currentMessage.trim()) {
|
|
12088
|
-
return commentSection;
|
|
12089
|
-
}
|
|
12090
|
-
return commentSection + '\n\n' + currentMessage;
|
|
12091
|
-
});
|
|
12092
|
-
}
|
|
12093
|
-
else {
|
|
12094
|
-
return this.appendToSystemMessage(requirements, commentSection);
|
|
12095
|
-
}
|
|
12096
|
-
}
|
|
12097
|
-
/**
|
|
12098
|
-
* Gets tool function implementations provided by this commitment
|
|
12099
|
-
*
|
|
12100
|
-
* When the `applyToAgentModelRequirements` adds tools to the requirements, this method should return the corresponding function definitions.
|
|
12101
|
-
*/
|
|
12102
|
-
getToolFunctions() {
|
|
12103
|
-
return {};
|
|
12104
|
-
}
|
|
12105
|
-
/**
|
|
12106
|
-
* Gets human-readable titles for tool functions provided by this commitment
|
|
12107
|
-
*
|
|
12108
|
-
* This is used in the UI to show a user-friendly name instead of the technical function name.
|
|
12109
|
-
*/
|
|
12110
|
-
getToolTitles() {
|
|
12111
|
-
return {};
|
|
12112
|
-
}
|
|
12113
|
-
}
|
|
12114
|
-
|
|
12115
|
-
/**
|
|
12116
|
-
* ACTION commitment definition
|
|
12117
|
-
*
|
|
12118
|
-
* The ACTION commitment defines specific actions or capabilities that the agent can perform.
|
|
12119
|
-
* This helps define what the agent is capable of doing and how it should approach tasks.
|
|
12075
|
+
* The DELETE commitment (and its aliases CANCEL, DISCARD, REMOVE) is used to
|
|
12076
|
+
* remove or disregard certain information or context. This can be useful for
|
|
12077
|
+
* overriding previous commitments or removing unwanted behaviors.
|
|
12120
12078
|
*
|
|
12121
12079
|
* Example usage in agent source:
|
|
12122
12080
|
*
|
|
12123
12081
|
* ```book
|
|
12124
|
-
*
|
|
12125
|
-
*
|
|
12082
|
+
* DELETE Previous formatting requirements
|
|
12083
|
+
* CANCEL All emotional responses
|
|
12084
|
+
* DISCARD Technical jargon explanations
|
|
12085
|
+
* REMOVE Casual conversational style
|
|
12126
12086
|
* ```
|
|
12127
12087
|
*
|
|
12128
12088
|
* @private [🪔] Maybe export the commitments through some package
|
|
12129
12089
|
*/
|
|
12130
|
-
class
|
|
12131
|
-
constructor(type
|
|
12090
|
+
class DeleteCommitmentDefinition extends BaseCommitmentDefinition {
|
|
12091
|
+
constructor(type) {
|
|
12132
12092
|
super(type);
|
|
12133
12093
|
}
|
|
12134
12094
|
/**
|
|
12135
|
-
* Short one-line description of
|
|
12095
|
+
* Short one-line description of DELETE/CANCEL/DISCARD/REMOVE.
|
|
12136
12096
|
*/
|
|
12137
12097
|
get description() {
|
|
12138
|
-
return '
|
|
12098
|
+
return 'Remove or **disregard** certain information, context, or previous commitments.';
|
|
12139
12099
|
}
|
|
12140
12100
|
/**
|
|
12141
12101
|
* Icon for this commitment.
|
|
12142
12102
|
*/
|
|
12143
12103
|
get icon() {
|
|
12144
|
-
return '
|
|
12104
|
+
return '🗑️';
|
|
12145
12105
|
}
|
|
12146
12106
|
/**
|
|
12147
|
-
* Markdown documentation for
|
|
12107
|
+
* Markdown documentation for DELETE commitment.
|
|
12148
12108
|
*/
|
|
12149
12109
|
get documentation() {
|
|
12150
12110
|
return spaceTrim$1(`
|
|
12151
|
-
#
|
|
12111
|
+
# DELETE (CANCEL, DISCARD, REMOVE)
|
|
12152
12112
|
|
|
12153
|
-
|
|
12113
|
+
A commitment to remove or disregard certain information or context. This can be useful for overriding previous commitments or removing unwanted behaviors.
|
|
12114
|
+
|
|
12115
|
+
## Aliases
|
|
12116
|
+
|
|
12117
|
+
- \`DELETE\` - Remove or eliminate something
|
|
12118
|
+
- \`CANCEL\` - Cancel or nullify something
|
|
12119
|
+
- \`DISCARD\` - Discard or ignore something
|
|
12120
|
+
- \`REMOVE\` - Remove or take away something
|
|
12154
12121
|
|
|
12155
12122
|
## Key aspects
|
|
12156
12123
|
|
|
12157
|
-
-
|
|
12158
|
-
-
|
|
12159
|
-
-
|
|
12124
|
+
- Multiple delete commitments can be used to remove different aspects.
|
|
12125
|
+
- Useful for overriding previous commitments in the same agent definition.
|
|
12126
|
+
- Can be used to remove inherited behaviors from base personas.
|
|
12127
|
+
- Helps fine-tune agent behavior by explicitly removing unwanted elements.
|
|
12128
|
+
|
|
12129
|
+
## Use cases
|
|
12130
|
+
|
|
12131
|
+
- Overriding inherited persona characteristics
|
|
12132
|
+
- Removing conflicting or outdated instructions
|
|
12133
|
+
- Disabling specific response patterns
|
|
12134
|
+
- Canceling previous formatting or style requirements
|
|
12160
12135
|
|
|
12161
12136
|
## Examples
|
|
12162
12137
|
|
|
12163
12138
|
\`\`\`book
|
|
12164
|
-
|
|
12139
|
+
Serious Business Assistant
|
|
12165
12140
|
|
|
12166
|
-
PERSONA You are a
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
|
|
12141
|
+
PERSONA You are a friendly and casual assistant who uses emojis
|
|
12142
|
+
DELETE Casual conversational style
|
|
12143
|
+
REMOVE All emoji usage
|
|
12144
|
+
GOAL Provide professional business communications
|
|
12145
|
+
STYLE Use formal language and proper business etiquette
|
|
12170
12146
|
\`\`\`
|
|
12171
12147
|
|
|
12172
12148
|
\`\`\`book
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
PERSONA You are a data analysis expert
|
|
12176
|
-
ACTION Able to analyze data and provide insights
|
|
12177
|
-
ACTION Can create visualizations and charts
|
|
12178
|
-
ACTION Capable of statistical analysis and modeling
|
|
12179
|
-
KNOWLEDGE Data analysis best practices and statistical methods
|
|
12180
|
-
\`\`\`
|
|
12181
|
-
`);
|
|
12182
|
-
}
|
|
12183
|
-
applyToAgentModelRequirements(requirements, content) {
|
|
12184
|
-
const trimmedContent = content.trim();
|
|
12185
|
-
if (!trimmedContent) {
|
|
12186
|
-
return requirements;
|
|
12187
|
-
}
|
|
12188
|
-
// Add action capability to the system message
|
|
12189
|
-
const actionSection = `Capability: ${trimmedContent}`;
|
|
12190
|
-
return this.appendToSystemMessage(requirements, actionSection, '\n\n');
|
|
12191
|
-
}
|
|
12192
|
-
}
|
|
12193
|
-
/**
|
|
12194
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
12195
|
-
*/
|
|
12196
|
-
|
|
12197
|
-
/**
|
|
12198
|
-
* CLOSED commitment definition
|
|
12199
|
-
*
|
|
12200
|
-
* The CLOSED commitment specifies that the agent CANNOT be modified by conversation.
|
|
12201
|
-
* It prevents the agent from learning from interactions and updating its source code.
|
|
12202
|
-
*
|
|
12203
|
-
* Example usage in agent source:
|
|
12204
|
-
*
|
|
12205
|
-
* ```book
|
|
12206
|
-
* CLOSED
|
|
12207
|
-
* ```
|
|
12208
|
-
*
|
|
12209
|
-
* @private [🪔] Maybe export the commitments through some package
|
|
12210
|
-
*/
|
|
12211
|
-
class ClosedCommitmentDefinition extends BaseCommitmentDefinition {
|
|
12212
|
-
constructor() {
|
|
12213
|
-
super('CLOSED');
|
|
12214
|
-
}
|
|
12215
|
-
/**
|
|
12216
|
-
* The `CLOSED` commitment is standalone.
|
|
12217
|
-
*/
|
|
12218
|
-
get requiresContent() {
|
|
12219
|
-
return false;
|
|
12220
|
-
}
|
|
12221
|
-
/**
|
|
12222
|
-
* Short one-line description of CLOSED.
|
|
12223
|
-
*/
|
|
12224
|
-
get description() {
|
|
12225
|
-
return 'Prevent the agent from being modified by conversation.';
|
|
12226
|
-
}
|
|
12227
|
-
/**
|
|
12228
|
-
* Icon for this commitment.
|
|
12229
|
-
*/
|
|
12230
|
-
get icon() {
|
|
12231
|
-
return '🔒';
|
|
12232
|
-
}
|
|
12233
|
-
/**
|
|
12234
|
-
* Markdown documentation for CLOSED commitment.
|
|
12235
|
-
*/
|
|
12236
|
-
get documentation() {
|
|
12237
|
-
return spaceTrim$1(`
|
|
12238
|
-
# CLOSED
|
|
12239
|
-
|
|
12240
|
-
Specifies that the agent **cannot** be modified by conversation with it.
|
|
12241
|
-
This means the agent will **not** learn from interactions and its source code will remain static during conversation.
|
|
12242
|
-
|
|
12243
|
-
By default (if not specified), agents are \`OPEN\` to modification.
|
|
12244
|
-
|
|
12245
|
-
> See also [OPEN](/docs/OPEN)
|
|
12246
|
-
|
|
12247
|
-
## Example
|
|
12248
|
-
|
|
12249
|
-
\`\`\`book
|
|
12250
|
-
CLOSED
|
|
12251
|
-
\`\`\`
|
|
12252
|
-
`);
|
|
12253
|
-
}
|
|
12254
|
-
applyToAgentModelRequirements(requirements, _content) {
|
|
12255
|
-
const updatedMetadata = {
|
|
12256
|
-
...requirements.metadata,
|
|
12257
|
-
isClosed: true,
|
|
12258
|
-
};
|
|
12259
|
-
return {
|
|
12260
|
-
...requirements,
|
|
12261
|
-
metadata: updatedMetadata,
|
|
12262
|
-
};
|
|
12263
|
-
}
|
|
12264
|
-
}
|
|
12265
|
-
/**
|
|
12266
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
12267
|
-
*/
|
|
12268
|
-
|
|
12269
|
-
/**
|
|
12270
|
-
* COMPONENT commitment definition
|
|
12271
|
-
*
|
|
12272
|
-
* The COMPONENT commitment defines a UI component that the agent can render in the chat.
|
|
12273
|
-
*
|
|
12274
|
-
* @private [🪔] Maybe export the commitments through some package
|
|
12275
|
-
*/
|
|
12276
|
-
class ComponentCommitmentDefinition extends BaseCommitmentDefinition {
|
|
12277
|
-
constructor() {
|
|
12278
|
-
super('COMPONENT');
|
|
12279
|
-
}
|
|
12280
|
-
/**
|
|
12281
|
-
* Short one-line description of COMPONENT.
|
|
12282
|
-
*/
|
|
12283
|
-
get description() {
|
|
12284
|
-
return 'Define a UI component that the agent can render in the chat.';
|
|
12285
|
-
}
|
|
12286
|
-
/**
|
|
12287
|
-
* Icon for this commitment.
|
|
12288
|
-
*/
|
|
12289
|
-
get icon() {
|
|
12290
|
-
return '🧩';
|
|
12291
|
-
}
|
|
12292
|
-
/**
|
|
12293
|
-
* Markdown documentation for COMPONENT commitment.
|
|
12294
|
-
*/
|
|
12295
|
-
get documentation() {
|
|
12296
|
-
return spaceTrim$1(`
|
|
12297
|
-
# COMPONENT
|
|
12298
|
-
|
|
12299
|
-
Defines a UI component that the agent can render in the chat.
|
|
12300
|
-
|
|
12301
|
-
## Key aspects
|
|
12302
|
-
|
|
12303
|
-
- Tells the agent that a specific component is available.
|
|
12304
|
-
- Provides syntax for using the component.
|
|
12305
|
-
|
|
12306
|
-
## Example
|
|
12307
|
-
|
|
12308
|
-
\`\`\`book
|
|
12309
|
-
COMPONENT Arrow
|
|
12310
|
-
The agent should render an arrow component in the chat UI.
|
|
12311
|
-
Syntax:
|
|
12312
|
-
<Arrow direction="up" color="red" />
|
|
12313
|
-
\`\`\`
|
|
12314
|
-
`);
|
|
12315
|
-
}
|
|
12316
|
-
applyToAgentModelRequirements(requirements, content) {
|
|
12317
|
-
const trimmedContent = content.trim();
|
|
12318
|
-
if (!trimmedContent) {
|
|
12319
|
-
return requirements;
|
|
12320
|
-
}
|
|
12321
|
-
// Add component capability to the system message
|
|
12322
|
-
const componentSection = `Component: ${trimmedContent}`;
|
|
12323
|
-
return this.appendToSystemMessage(requirements, componentSection, '\n\n');
|
|
12324
|
-
}
|
|
12325
|
-
}
|
|
12326
|
-
/**
|
|
12327
|
-
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
12328
|
-
*/
|
|
12329
|
-
|
|
12330
|
-
/**
|
|
12331
|
-
* DELETE commitment definition
|
|
12332
|
-
*
|
|
12333
|
-
* The DELETE commitment (and its aliases CANCEL, DISCARD, REMOVE) is used to
|
|
12334
|
-
* remove or disregard certain information or context. This can be useful for
|
|
12335
|
-
* overriding previous commitments or removing unwanted behaviors.
|
|
12336
|
-
*
|
|
12337
|
-
* Example usage in agent source:
|
|
12338
|
-
*
|
|
12339
|
-
* ```book
|
|
12340
|
-
* DELETE Previous formatting requirements
|
|
12341
|
-
* CANCEL All emotional responses
|
|
12342
|
-
* DISCARD Technical jargon explanations
|
|
12343
|
-
* REMOVE Casual conversational style
|
|
12344
|
-
* ```
|
|
12345
|
-
*
|
|
12346
|
-
* @private [🪔] Maybe export the commitments through some package
|
|
12347
|
-
*/
|
|
12348
|
-
class DeleteCommitmentDefinition extends BaseCommitmentDefinition {
|
|
12349
|
-
constructor(type) {
|
|
12350
|
-
super(type);
|
|
12351
|
-
}
|
|
12352
|
-
/**
|
|
12353
|
-
* Short one-line description of DELETE/CANCEL/DISCARD/REMOVE.
|
|
12354
|
-
*/
|
|
12355
|
-
get description() {
|
|
12356
|
-
return 'Remove or **disregard** certain information, context, or previous commitments.';
|
|
12357
|
-
}
|
|
12358
|
-
/**
|
|
12359
|
-
* Icon for this commitment.
|
|
12360
|
-
*/
|
|
12361
|
-
get icon() {
|
|
12362
|
-
return '🗑️';
|
|
12363
|
-
}
|
|
12364
|
-
/**
|
|
12365
|
-
* Markdown documentation for DELETE commitment.
|
|
12366
|
-
*/
|
|
12367
|
-
get documentation() {
|
|
12368
|
-
return spaceTrim$1(`
|
|
12369
|
-
# DELETE (CANCEL, DISCARD, REMOVE)
|
|
12370
|
-
|
|
12371
|
-
A commitment to remove or disregard certain information or context. This can be useful for overriding previous commitments or removing unwanted behaviors.
|
|
12372
|
-
|
|
12373
|
-
## Aliases
|
|
12374
|
-
|
|
12375
|
-
- \`DELETE\` - Remove or eliminate something
|
|
12376
|
-
- \`CANCEL\` - Cancel or nullify something
|
|
12377
|
-
- \`DISCARD\` - Discard or ignore something
|
|
12378
|
-
- \`REMOVE\` - Remove or take away something
|
|
12379
|
-
|
|
12380
|
-
## Key aspects
|
|
12381
|
-
|
|
12382
|
-
- Multiple delete commitments can be used to remove different aspects.
|
|
12383
|
-
- Useful for overriding previous commitments in the same agent definition.
|
|
12384
|
-
- Can be used to remove inherited behaviors from base personas.
|
|
12385
|
-
- Helps fine-tune agent behavior by explicitly removing unwanted elements.
|
|
12386
|
-
|
|
12387
|
-
## Use cases
|
|
12388
|
-
|
|
12389
|
-
- Overriding inherited persona characteristics
|
|
12390
|
-
- Removing conflicting or outdated instructions
|
|
12391
|
-
- Disabling specific response patterns
|
|
12392
|
-
- Canceling previous formatting or style requirements
|
|
12393
|
-
|
|
12394
|
-
## Examples
|
|
12395
|
-
|
|
12396
|
-
\`\`\`book
|
|
12397
|
-
Serious Business Assistant
|
|
12398
|
-
|
|
12399
|
-
PERSONA You are a friendly and casual assistant who uses emojis
|
|
12400
|
-
DELETE Casual conversational style
|
|
12401
|
-
REMOVE All emoji usage
|
|
12402
|
-
GOAL Provide professional business communications
|
|
12403
|
-
STYLE Use formal language and proper business etiquette
|
|
12404
|
-
\`\`\`
|
|
12405
|
-
|
|
12406
|
-
\`\`\`book
|
|
12407
|
-
Simplified Technical Support
|
|
12149
|
+
Simplified Technical Support
|
|
12408
12150
|
|
|
12409
12151
|
PERSONA You are a technical support specialist with deep expertise
|
|
12410
12152
|
KNOWLEDGE Extensive database of technical specifications
|
|
@@ -15354,50 +15096,173 @@ function stripToolCallLines(text) {
|
|
|
15354
15096
|
*/
|
|
15355
15097
|
|
|
15356
15098
|
/**
|
|
15357
|
-
*
|
|
15358
|
-
*
|
|
15359
|
-
* The USE commitment indicates that the agent should utilize specific tools or capabilities
|
|
15360
|
-
* to access and interact with external systems when necessary.
|
|
15361
|
-
*
|
|
15362
|
-
* Supported USE types:
|
|
15363
|
-
* - USE BROWSER: Enables the agent to use a web browser tool
|
|
15364
|
-
* - USE SEARCH ENGINE (future): Enables search engine access
|
|
15365
|
-
* - USE FILE SYSTEM (future): Enables file system operations
|
|
15366
|
-
* - USE MCP (future): Enables MCP server connections
|
|
15099
|
+
* TEMPLATE commitment definition
|
|
15367
15100
|
*
|
|
15368
|
-
* The
|
|
15101
|
+
* The TEMPLATE commitment enforces a specific response structure or template
|
|
15102
|
+
* that the agent must follow when generating responses. This helps ensure
|
|
15103
|
+
* consistent message formatting across all agent interactions.
|
|
15369
15104
|
*
|
|
15370
15105
|
* Example usage in agent source:
|
|
15371
15106
|
*
|
|
15372
15107
|
* ```book
|
|
15373
|
-
*
|
|
15374
|
-
*
|
|
15108
|
+
* TEMPLATE Always structure your response with: 1) Summary, 2) Details, 3) Next steps
|
|
15109
|
+
* TEMPLATE Use the following format: **Question:** [user question] | **Answer:** [your answer]
|
|
15375
15110
|
* ```
|
|
15376
15111
|
*
|
|
15112
|
+
* When used without content, it enables template mode which instructs the agent
|
|
15113
|
+
* to follow any template patterns defined in other commitments or context.
|
|
15114
|
+
*
|
|
15377
15115
|
* @private [🪔] Maybe export the commitments through some package
|
|
15378
15116
|
*/
|
|
15379
|
-
class
|
|
15380
|
-
constructor() {
|
|
15381
|
-
super(
|
|
15117
|
+
class TemplateCommitmentDefinition extends BaseCommitmentDefinition {
|
|
15118
|
+
constructor(type = 'TEMPLATE') {
|
|
15119
|
+
super(type);
|
|
15382
15120
|
}
|
|
15383
15121
|
/**
|
|
15384
|
-
* Short one-line description of
|
|
15122
|
+
* Short one-line description of TEMPLATE.
|
|
15385
15123
|
*/
|
|
15386
15124
|
get description() {
|
|
15387
|
-
return '
|
|
15125
|
+
return 'Enforce a specific message structure or response template.';
|
|
15388
15126
|
}
|
|
15389
15127
|
/**
|
|
15390
15128
|
* Icon for this commitment.
|
|
15391
15129
|
*/
|
|
15392
15130
|
get icon() {
|
|
15393
|
-
return '
|
|
15131
|
+
return '📋';
|
|
15394
15132
|
}
|
|
15395
15133
|
/**
|
|
15396
|
-
* Markdown documentation for
|
|
15134
|
+
* Markdown documentation for TEMPLATE commitment.
|
|
15397
15135
|
*/
|
|
15398
15136
|
get documentation() {
|
|
15399
15137
|
return spaceTrim$1(`
|
|
15400
|
-
#
|
|
15138
|
+
# ${this.type}
|
|
15139
|
+
|
|
15140
|
+
Enforces a specific response structure or template that the agent must follow when generating responses.
|
|
15141
|
+
|
|
15142
|
+
## Key aspects
|
|
15143
|
+
|
|
15144
|
+
- Both terms work identically and can be used interchangeably.
|
|
15145
|
+
- Can be used with or without content.
|
|
15146
|
+
- When used without content, enables template mode for structured responses.
|
|
15147
|
+
- When used with content, defines the specific template structure to follow.
|
|
15148
|
+
- Multiple templates can be combined, with later ones taking precedence.
|
|
15149
|
+
|
|
15150
|
+
## Examples
|
|
15151
|
+
|
|
15152
|
+
\`\`\`book
|
|
15153
|
+
Customer Support Agent
|
|
15154
|
+
|
|
15155
|
+
PERSONA You are a helpful customer support representative
|
|
15156
|
+
TEMPLATE Always structure your response with: 1) Acknowledgment, 2) Solution, 3) Follow-up question
|
|
15157
|
+
STYLE Be professional and empathetic
|
|
15158
|
+
\`\`\`
|
|
15159
|
+
|
|
15160
|
+
\`\`\`book
|
|
15161
|
+
Technical Documentation Assistant
|
|
15162
|
+
|
|
15163
|
+
PERSONA You are a technical writing expert
|
|
15164
|
+
TEMPLATE Use the following format: **Topic:** [topic] | **Explanation:** [details] | **Example:** [code]
|
|
15165
|
+
FORMAT Use markdown with clear headings
|
|
15166
|
+
\`\`\`
|
|
15167
|
+
|
|
15168
|
+
\`\`\`book
|
|
15169
|
+
Simple Agent
|
|
15170
|
+
|
|
15171
|
+
PERSONA You are a virtual assistant
|
|
15172
|
+
TEMPLATE
|
|
15173
|
+
\`\`\`
|
|
15174
|
+
`);
|
|
15175
|
+
}
|
|
15176
|
+
/**
|
|
15177
|
+
* TEMPLATE can be used with or without content.
|
|
15178
|
+
*/
|
|
15179
|
+
get requiresContent() {
|
|
15180
|
+
return false;
|
|
15181
|
+
}
|
|
15182
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
15183
|
+
var _a;
|
|
15184
|
+
const trimmedContent = content.trim();
|
|
15185
|
+
// If no content is provided, enable template mode
|
|
15186
|
+
if (!trimmedContent) {
|
|
15187
|
+
// Store template mode flag in metadata
|
|
15188
|
+
const updatedMetadata = {
|
|
15189
|
+
...requirements.metadata,
|
|
15190
|
+
templateMode: true,
|
|
15191
|
+
};
|
|
15192
|
+
// Add a general instruction about using structured templates
|
|
15193
|
+
const templateModeInstruction = spaceTrim$1(`
|
|
15194
|
+
Use a clear, structured template format for your responses.
|
|
15195
|
+
Maintain consistency in how you organize and present information.
|
|
15196
|
+
`);
|
|
15197
|
+
return {
|
|
15198
|
+
...this.appendToSystemMessage(requirements, templateModeInstruction, '\n\n'),
|
|
15199
|
+
metadata: updatedMetadata,
|
|
15200
|
+
};
|
|
15201
|
+
}
|
|
15202
|
+
// If content is provided, add the specific template instructions
|
|
15203
|
+
const templateSection = `Response Template: ${trimmedContent}`;
|
|
15204
|
+
// Store the template in metadata for potential programmatic access
|
|
15205
|
+
const existingTemplates = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.templates) || [];
|
|
15206
|
+
const updatedMetadata = {
|
|
15207
|
+
...requirements.metadata,
|
|
15208
|
+
templates: [...existingTemplates, trimmedContent],
|
|
15209
|
+
templateMode: true,
|
|
15210
|
+
};
|
|
15211
|
+
return {
|
|
15212
|
+
...this.appendToSystemMessage(requirements, templateSection, '\n\n'),
|
|
15213
|
+
metadata: updatedMetadata,
|
|
15214
|
+
};
|
|
15215
|
+
}
|
|
15216
|
+
}
|
|
15217
|
+
/**
|
|
15218
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
15219
|
+
*/
|
|
15220
|
+
|
|
15221
|
+
/**
|
|
15222
|
+
* USE commitment definition
|
|
15223
|
+
*
|
|
15224
|
+
* The USE commitment indicates that the agent should utilize specific tools or capabilities
|
|
15225
|
+
* to access and interact with external systems when necessary.
|
|
15226
|
+
*
|
|
15227
|
+
* Supported USE types:
|
|
15228
|
+
* - USE BROWSER: Enables the agent to use a web browser tool
|
|
15229
|
+
* - USE SEARCH ENGINE (future): Enables search engine access
|
|
15230
|
+
* - USE FILE SYSTEM (future): Enables file system operations
|
|
15231
|
+
* - USE MCP (future): Enables MCP server connections
|
|
15232
|
+
*
|
|
15233
|
+
* The content following the USE commitment is ignored (similar to NOTE).
|
|
15234
|
+
*
|
|
15235
|
+
* Example usage in agent source:
|
|
15236
|
+
*
|
|
15237
|
+
* ```book
|
|
15238
|
+
* USE BROWSER
|
|
15239
|
+
* USE SEARCH ENGINE
|
|
15240
|
+
* ```
|
|
15241
|
+
*
|
|
15242
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
15243
|
+
*/
|
|
15244
|
+
class UseCommitmentDefinition extends BaseCommitmentDefinition {
|
|
15245
|
+
constructor() {
|
|
15246
|
+
super('USE');
|
|
15247
|
+
}
|
|
15248
|
+
/**
|
|
15249
|
+
* Short one-line description of USE commitments.
|
|
15250
|
+
*/
|
|
15251
|
+
get description() {
|
|
15252
|
+
return 'Enable the agent to use specific tools or capabilities (BROWSER, SEARCH ENGINE, etc.).';
|
|
15253
|
+
}
|
|
15254
|
+
/**
|
|
15255
|
+
* Icon for this commitment.
|
|
15256
|
+
*/
|
|
15257
|
+
get icon() {
|
|
15258
|
+
return '🔧';
|
|
15259
|
+
}
|
|
15260
|
+
/**
|
|
15261
|
+
* Markdown documentation for USE commitment.
|
|
15262
|
+
*/
|
|
15263
|
+
get documentation() {
|
|
15264
|
+
return spaceTrim$1(`
|
|
15265
|
+
# USE
|
|
15401
15266
|
|
|
15402
15267
|
Enables the agent to use specific tools or capabilities for interacting with external systems.
|
|
15403
15268
|
|
|
@@ -15467,12 +15332,56 @@ class UseCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
15467
15332
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
15468
15333
|
*/
|
|
15469
15334
|
|
|
15335
|
+
/**
|
|
15336
|
+
* Client-side safe wrapper for fetching URL content
|
|
15337
|
+
*
|
|
15338
|
+
* This function proxies requests to the Agents Server API endpoint for scraping,
|
|
15339
|
+
* making it safe to use in browser environments.
|
|
15340
|
+
*
|
|
15341
|
+
* @param url The URL to fetch and scrape
|
|
15342
|
+
* @param agentsServerUrl The base URL of the agents server (defaults to current origin)
|
|
15343
|
+
* @returns Markdown content from the URL
|
|
15344
|
+
*
|
|
15345
|
+
* @private internal utility for USE BROWSER commitment
|
|
15346
|
+
*/
|
|
15347
|
+
async function fetchUrlContentViaBrowser(url, agentsServerUrl) {
|
|
15348
|
+
try {
|
|
15349
|
+
// Determine the agents server URL
|
|
15350
|
+
const baseUrl = agentsServerUrl || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
15351
|
+
if (!baseUrl) {
|
|
15352
|
+
throw new Error('Agents server URL is required in non-browser environments');
|
|
15353
|
+
}
|
|
15354
|
+
// Build the API endpoint URL
|
|
15355
|
+
const apiUrl = new URL('/api/scrape', baseUrl);
|
|
15356
|
+
apiUrl.searchParams.set('url', url);
|
|
15357
|
+
// Fetch from the API endpoint
|
|
15358
|
+
const response = await fetch(apiUrl.toString());
|
|
15359
|
+
if (!response.ok) {
|
|
15360
|
+
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
15361
|
+
throw new Error(`Failed to scrape URL: ${errorData.error || response.statusText}`);
|
|
15362
|
+
}
|
|
15363
|
+
const data = await response.json();
|
|
15364
|
+
if (!data.success || !data.content) {
|
|
15365
|
+
throw new Error(`Scraping failed: ${data.error || 'No content returned'}`);
|
|
15366
|
+
}
|
|
15367
|
+
return data.content;
|
|
15368
|
+
}
|
|
15369
|
+
catch (error) {
|
|
15370
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15371
|
+
throw new Error(`Error fetching URL content via browser: ${errorMessage}`);
|
|
15372
|
+
}
|
|
15373
|
+
}
|
|
15374
|
+
|
|
15470
15375
|
/**
|
|
15471
15376
|
* USE BROWSER commitment definition
|
|
15472
15377
|
*
|
|
15473
|
-
* The `USE BROWSER` commitment indicates that the agent should utilize
|
|
15378
|
+
* The `USE BROWSER` commitment indicates that the agent should utilize browser tools
|
|
15474
15379
|
* to access and retrieve up-to-date information from the internet when necessary.
|
|
15475
15380
|
*
|
|
15381
|
+
* This commitment provides two levels of browser access:
|
|
15382
|
+
* 1. One-shot URL fetching: Simple function to fetch and scrape URL content
|
|
15383
|
+
* 2. Running browser: For complex tasks like scrolling, clicking, etc. (prepared but not active yet)
|
|
15384
|
+
*
|
|
15476
15385
|
* The content following `USE BROWSER` is ignored (similar to NOTE).
|
|
15477
15386
|
*
|
|
15478
15387
|
* Example usage in agent source:
|
|
@@ -15498,7 +15407,7 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
15498
15407
|
* Short one-line description of USE BROWSER.
|
|
15499
15408
|
*/
|
|
15500
15409
|
get description() {
|
|
15501
|
-
return 'Enable the agent to use
|
|
15410
|
+
return 'Enable the agent to use browser tools for accessing internet information.';
|
|
15502
15411
|
}
|
|
15503
15412
|
/**
|
|
15504
15413
|
* Icon for this commitment.
|
|
@@ -15513,14 +15422,18 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
15513
15422
|
return spaceTrim$1(`
|
|
15514
15423
|
# USE BROWSER
|
|
15515
15424
|
|
|
15516
|
-
Enables the agent to use
|
|
15425
|
+
Enables the agent to use browser tools to access and retrieve up-to-date information from the internet.
|
|
15517
15426
|
|
|
15518
15427
|
## Key aspects
|
|
15519
15428
|
|
|
15520
15429
|
- The content following \`USE BROWSER\` is ignored (similar to NOTE)
|
|
15430
|
+
- Provides two levels of browser access:
|
|
15431
|
+
1. **One-shot URL fetching**: Simple function to fetch and scrape URL content (active)
|
|
15432
|
+
2. **Running browser**: For complex tasks like scrolling, clicking, etc. (prepared but not active yet)
|
|
15521
15433
|
- The actual browser tool usage is handled by the agent runtime
|
|
15522
|
-
- Allows the agent to fetch current information from websites
|
|
15434
|
+
- Allows the agent to fetch current information from websites and documents
|
|
15523
15435
|
- Useful for research tasks, fact-checking, and accessing dynamic content
|
|
15436
|
+
- Supports various content types including HTML pages and PDF documents
|
|
15524
15437
|
|
|
15525
15438
|
## Examples
|
|
15526
15439
|
|
|
@@ -15556,48 +15469,306 @@ class UseBrowserCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
15556
15469
|
*/
|
|
15557
15470
|
getToolTitles() {
|
|
15558
15471
|
return {
|
|
15559
|
-
|
|
15472
|
+
fetch_url_content: 'Fetch URL content',
|
|
15473
|
+
run_browser: 'Run browser',
|
|
15474
|
+
};
|
|
15475
|
+
}
|
|
15476
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
15477
|
+
// Get existing tools array or create new one
|
|
15478
|
+
const existingTools = requirements.tools || [];
|
|
15479
|
+
// Add browser tools if not already present
|
|
15480
|
+
const toolsToAdd = [];
|
|
15481
|
+
// Tool 1: One-shot URL content fetching
|
|
15482
|
+
if (!existingTools.some((tool) => tool.name === 'fetch_url_content')) {
|
|
15483
|
+
toolsToAdd.push({
|
|
15484
|
+
name: 'fetch_url_content',
|
|
15485
|
+
description: spaceTrim$1(`
|
|
15486
|
+
Fetches and scrapes the content from a URL (webpage or document).
|
|
15487
|
+
This tool retrieves the content of the specified URL and converts it to markdown format.
|
|
15488
|
+
Use this when you need to access information from a specific website or document.
|
|
15489
|
+
Supports various content types including HTML pages and PDF documents.
|
|
15490
|
+
`),
|
|
15491
|
+
parameters: {
|
|
15492
|
+
type: 'object',
|
|
15493
|
+
properties: {
|
|
15494
|
+
url: {
|
|
15495
|
+
type: 'string',
|
|
15496
|
+
description: 'The URL to fetch and scrape (e.g., "https://example.com" or "https://example.com/document.pdf")',
|
|
15497
|
+
},
|
|
15498
|
+
},
|
|
15499
|
+
required: ['url'],
|
|
15500
|
+
},
|
|
15501
|
+
});
|
|
15502
|
+
}
|
|
15503
|
+
// Tool 2: Running browser (prepared but not active yet)
|
|
15504
|
+
if (!existingTools.some((tool) => tool.name === 'run_browser')) {
|
|
15505
|
+
toolsToAdd.push({
|
|
15506
|
+
name: 'run_browser',
|
|
15507
|
+
description: spaceTrim$1(`
|
|
15508
|
+
Launches a browser session for complex interactions.
|
|
15509
|
+
This tool is for advanced browser automation tasks like scrolling, clicking, form filling, etc.
|
|
15510
|
+
Note: This tool is prepared but not yet active. It will be implemented in a future update.
|
|
15511
|
+
`),
|
|
15512
|
+
parameters: {
|
|
15513
|
+
type: 'object',
|
|
15514
|
+
properties: {
|
|
15515
|
+
url: {
|
|
15516
|
+
type: 'string',
|
|
15517
|
+
description: 'The initial URL to navigate to',
|
|
15518
|
+
},
|
|
15519
|
+
actions: {
|
|
15520
|
+
type: 'array',
|
|
15521
|
+
description: 'Array of actions to perform in the browser',
|
|
15522
|
+
items: {
|
|
15523
|
+
type: 'object',
|
|
15524
|
+
properties: {
|
|
15525
|
+
type: {
|
|
15526
|
+
type: 'string',
|
|
15527
|
+
enum: ['navigate', 'click', 'scroll', 'type', 'wait'],
|
|
15528
|
+
},
|
|
15529
|
+
selector: {
|
|
15530
|
+
type: 'string',
|
|
15531
|
+
description: 'CSS selector for the element (for click, type actions)',
|
|
15532
|
+
},
|
|
15533
|
+
value: {
|
|
15534
|
+
type: 'string',
|
|
15535
|
+
description: 'Value to type or navigate to',
|
|
15536
|
+
},
|
|
15537
|
+
},
|
|
15538
|
+
},
|
|
15539
|
+
},
|
|
15540
|
+
},
|
|
15541
|
+
required: ['url'],
|
|
15542
|
+
},
|
|
15543
|
+
});
|
|
15544
|
+
}
|
|
15545
|
+
const updatedTools = [...existingTools, ...toolsToAdd];
|
|
15546
|
+
// Return requirements with updated tools and metadata
|
|
15547
|
+
return this.appendToSystemMessage({
|
|
15548
|
+
...requirements,
|
|
15549
|
+
tools: updatedTools,
|
|
15550
|
+
metadata: {
|
|
15551
|
+
...requirements.metadata,
|
|
15552
|
+
useBrowser: true,
|
|
15553
|
+
},
|
|
15554
|
+
}, spaceTrim$1(`
|
|
15555
|
+
You have access to browser tools to fetch and access content from the internet.
|
|
15556
|
+
- Use "fetch_url_content" to retrieve content from specific URLs (webpages or documents)
|
|
15557
|
+
- Use "run_browser" for complex browser interactions (note: not yet active)
|
|
15558
|
+
When you need to know information from a specific website or document, use the fetch_url_content tool.
|
|
15559
|
+
`));
|
|
15560
|
+
}
|
|
15561
|
+
/**
|
|
15562
|
+
* Gets the browser tool function implementations.
|
|
15563
|
+
*
|
|
15564
|
+
* This method automatically detects the environment and uses:
|
|
15565
|
+
* - Server-side: Direct scraping via fetchUrlContent (Node.js)
|
|
15566
|
+
* - Browser: Proxy through Agents Server API via fetchUrlContentViaBrowser
|
|
15567
|
+
*/
|
|
15568
|
+
getToolFunctions() {
|
|
15569
|
+
return {
|
|
15570
|
+
/**
|
|
15571
|
+
* @@@
|
|
15572
|
+
*
|
|
15573
|
+
* Note: [🛺] This function has implementation both for browser and node, this is the proxied one for browser
|
|
15574
|
+
*/
|
|
15575
|
+
async fetch_url_content(args) {
|
|
15576
|
+
console.log('!!!! [Tool] fetch_url_content called', { args });
|
|
15577
|
+
const { url } = args;
|
|
15578
|
+
return await fetchUrlContentViaBrowser(url);
|
|
15579
|
+
},
|
|
15580
|
+
/**
|
|
15581
|
+
* @@@
|
|
15582
|
+
*/
|
|
15583
|
+
async run_browser(args) {
|
|
15584
|
+
console.log('!!!! [Tool] run_browser called', { args });
|
|
15585
|
+
const { url } = args;
|
|
15586
|
+
// This tool is prepared but not active yet
|
|
15587
|
+
return spaceTrim$1(`
|
|
15588
|
+
# Running browser
|
|
15589
|
+
|
|
15590
|
+
The running browser tool is not yet active.
|
|
15591
|
+
|
|
15592
|
+
Requested URL: ${url}
|
|
15593
|
+
|
|
15594
|
+
This feature will be implemented in a future update to support:
|
|
15595
|
+
- Complex browser interactions
|
|
15596
|
+
- Scrolling and navigation
|
|
15597
|
+
- Clicking and form filling
|
|
15598
|
+
- Taking screenshots
|
|
15599
|
+
|
|
15600
|
+
For now, please use the "fetch_url_content" tool instead.
|
|
15601
|
+
`);
|
|
15602
|
+
},
|
|
15560
15603
|
};
|
|
15561
15604
|
}
|
|
15605
|
+
}
|
|
15606
|
+
/**
|
|
15607
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
15608
|
+
*/
|
|
15609
|
+
|
|
15610
|
+
/**
|
|
15611
|
+
* @@@
|
|
15612
|
+
*
|
|
15613
|
+
* @private utility for commitments
|
|
15614
|
+
*/
|
|
15615
|
+
function formatOptionalInstructionBlock(label, content) {
|
|
15616
|
+
const trimmedContent = spaceTrim$1(content);
|
|
15617
|
+
if (!trimmedContent) {
|
|
15618
|
+
return '';
|
|
15619
|
+
}
|
|
15620
|
+
return spaceTrim$1((block) => `
|
|
15621
|
+
- ${label}:
|
|
15622
|
+
${block(trimmedContent
|
|
15623
|
+
.split('\n')
|
|
15624
|
+
.map((line) => `- ${line}`)
|
|
15625
|
+
.join('\n'))}
|
|
15626
|
+
`);
|
|
15627
|
+
}
|
|
15628
|
+
|
|
15629
|
+
/**
|
|
15630
|
+
* USE EMAIL commitment definition
|
|
15631
|
+
*
|
|
15632
|
+
* The `USE EMAIL` commitment enables the agent to send emails.
|
|
15633
|
+
*
|
|
15634
|
+
* Example usage in agent source:
|
|
15635
|
+
*
|
|
15636
|
+
* ```book
|
|
15637
|
+
* USE EMAIL
|
|
15638
|
+
* USE EMAIL Write always formal and polite emails, always greet.
|
|
15639
|
+
* ```
|
|
15640
|
+
*
|
|
15641
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
15642
|
+
*/
|
|
15643
|
+
class UseEmailCommitmentDefinition extends BaseCommitmentDefinition {
|
|
15644
|
+
constructor() {
|
|
15645
|
+
super('USE EMAIL', ['EMAIL', 'MAIL']);
|
|
15646
|
+
}
|
|
15647
|
+
get requiresContent() {
|
|
15648
|
+
return false;
|
|
15649
|
+
}
|
|
15650
|
+
/**
|
|
15651
|
+
* Short one-line description of USE EMAIL.
|
|
15652
|
+
*/
|
|
15653
|
+
get description() {
|
|
15654
|
+
return 'Enable the agent to send emails.';
|
|
15655
|
+
}
|
|
15656
|
+
/**
|
|
15657
|
+
* Icon for this commitment.
|
|
15658
|
+
*/
|
|
15659
|
+
get icon() {
|
|
15660
|
+
return '📧';
|
|
15661
|
+
}
|
|
15662
|
+
/**
|
|
15663
|
+
* Markdown documentation for USE EMAIL commitment.
|
|
15664
|
+
*/
|
|
15665
|
+
get documentation() {
|
|
15666
|
+
return spaceTrim$1(`
|
|
15667
|
+
# USE EMAIL
|
|
15668
|
+
|
|
15669
|
+
Enables the agent to send emails through the email service.
|
|
15670
|
+
|
|
15671
|
+
## Key aspects
|
|
15672
|
+
|
|
15673
|
+
- The agent can send emails to specified recipients.
|
|
15674
|
+
- Supports multiple recipients, CC, subject, and markdown content.
|
|
15675
|
+
- Emails are queued and sent through configured email providers.
|
|
15676
|
+
- The content following \`USE EMAIL\` can provide additional instructions for email composition (e.g., style, tone, formatting preferences).
|
|
15677
|
+
|
|
15678
|
+
## Examples
|
|
15679
|
+
|
|
15680
|
+
\`\`\`book
|
|
15681
|
+
Email Assistant
|
|
15682
|
+
|
|
15683
|
+
PERSONA You are a helpful assistant who can send emails.
|
|
15684
|
+
USE EMAIL
|
|
15685
|
+
\`\`\`
|
|
15686
|
+
|
|
15687
|
+
\`\`\`book
|
|
15688
|
+
Formal Email Assistant
|
|
15689
|
+
|
|
15690
|
+
PERSONA You help with professional communication.
|
|
15691
|
+
USE EMAIL Write always formal and polite emails, always greet.
|
|
15692
|
+
\`\`\`
|
|
15693
|
+
`);
|
|
15694
|
+
}
|
|
15562
15695
|
applyToAgentModelRequirements(requirements, content) {
|
|
15696
|
+
const extraInstructions = formatOptionalInstructionBlock('Email instructions', content);
|
|
15563
15697
|
// Get existing tools array or create new one
|
|
15564
15698
|
const existingTools = requirements.tools || [];
|
|
15565
|
-
// Add '
|
|
15566
|
-
const updatedTools = existingTools.some((tool) => tool.name === '
|
|
15699
|
+
// Add 'send_email' to tools if not already present
|
|
15700
|
+
const updatedTools = existingTools.some((tool) => tool.name === 'send_email')
|
|
15567
15701
|
? existingTools
|
|
15568
|
-
:
|
|
15569
|
-
// TODO: [🔰] Use through proper MCP server
|
|
15702
|
+
: [
|
|
15570
15703
|
...existingTools,
|
|
15571
15704
|
{
|
|
15572
|
-
name: '
|
|
15573
|
-
description:
|
|
15574
|
-
A tool that can browse the web.
|
|
15575
|
-
Use this tool when you need to access specific websites or browse the internet.
|
|
15576
|
-
`),
|
|
15705
|
+
name: 'send_email',
|
|
15706
|
+
description: `Send an email to one or more recipients. ${!content ? '' : `Style instructions: ${content}`}`,
|
|
15577
15707
|
parameters: {
|
|
15578
15708
|
type: 'object',
|
|
15579
15709
|
properties: {
|
|
15580
|
-
|
|
15710
|
+
to: {
|
|
15711
|
+
type: 'array',
|
|
15712
|
+
items: { type: 'string' },
|
|
15713
|
+
description: 'Array of recipient email addresses (e.g., ["user@example.com", "Jane Doe <jane@example.com>"])',
|
|
15714
|
+
},
|
|
15715
|
+
cc: {
|
|
15716
|
+
type: 'array',
|
|
15717
|
+
items: { type: 'string' },
|
|
15718
|
+
description: 'Optional array of CC email addresses',
|
|
15719
|
+
},
|
|
15720
|
+
subject: {
|
|
15581
15721
|
type: 'string',
|
|
15582
|
-
description: '
|
|
15722
|
+
description: 'Email subject line',
|
|
15723
|
+
},
|
|
15724
|
+
body: {
|
|
15725
|
+
type: 'string',
|
|
15726
|
+
description: 'Email body content in markdown format',
|
|
15583
15727
|
},
|
|
15584
15728
|
},
|
|
15585
|
-
required: ['
|
|
15729
|
+
required: ['to', 'subject', 'body'],
|
|
15586
15730
|
},
|
|
15587
15731
|
},
|
|
15588
|
-
|
|
15732
|
+
// <- TODO: !!!! define the function in LLM tools
|
|
15733
|
+
];
|
|
15589
15734
|
// Return requirements with updated tools and metadata
|
|
15590
15735
|
return this.appendToSystemMessage({
|
|
15591
15736
|
...requirements,
|
|
15592
15737
|
tools: updatedTools,
|
|
15593
15738
|
metadata: {
|
|
15594
15739
|
...requirements.metadata,
|
|
15595
|
-
|
|
15740
|
+
useEmail: content || true,
|
|
15596
15741
|
},
|
|
15597
|
-
}, spaceTrim$1(`
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15742
|
+
}, spaceTrim$1((block) => `
|
|
15743
|
+
Email tool:
|
|
15744
|
+
- You have access to send emails via the tool "send_email".
|
|
15745
|
+
- Use it when you need to send emails to users or other recipients.
|
|
15746
|
+
- The email body should be written in markdown format.
|
|
15747
|
+
- Always ensure the email content is clear, professional, and appropriate.
|
|
15748
|
+
${block(extraInstructions)}
|
|
15749
|
+
`));
|
|
15750
|
+
}
|
|
15751
|
+
/**
|
|
15752
|
+
* Gets human-readable titles for tool functions provided by this commitment.
|
|
15753
|
+
*/
|
|
15754
|
+
getToolTitles() {
|
|
15755
|
+
return {
|
|
15756
|
+
send_email: 'Send email',
|
|
15757
|
+
};
|
|
15758
|
+
}
|
|
15759
|
+
/**
|
|
15760
|
+
* Gets the `send_email` tool function implementation.
|
|
15761
|
+
* Note: This is a placeholder - the actual implementation is provided by the agent server.
|
|
15762
|
+
*/
|
|
15763
|
+
getToolFunctions() {
|
|
15764
|
+
return {
|
|
15765
|
+
async send_email(args) {
|
|
15766
|
+
console.log('!!!! [Tool] send_email called', { args });
|
|
15767
|
+
// This is a placeholder implementation
|
|
15768
|
+
// The actual implementation should be provided by the agent server
|
|
15769
|
+
throw new Error('send_email tool not implemented. This commitment requires integration with an email service.');
|
|
15770
|
+
},
|
|
15771
|
+
};
|
|
15601
15772
|
}
|
|
15602
15773
|
}
|
|
15603
15774
|
/**
|
|
@@ -15866,25 +16037,6 @@ class SerpSearchEngine {
|
|
|
15866
16037
|
}
|
|
15867
16038
|
}
|
|
15868
16039
|
|
|
15869
|
-
/**
|
|
15870
|
-
* @@@
|
|
15871
|
-
*
|
|
15872
|
-
* @private utility for commitments
|
|
15873
|
-
*/
|
|
15874
|
-
function formatOptionalInstructionBlock(label, content) {
|
|
15875
|
-
const trimmedContent = spaceTrim$1(content);
|
|
15876
|
-
if (!trimmedContent) {
|
|
15877
|
-
return '';
|
|
15878
|
-
}
|
|
15879
|
-
return spaceTrim$1((block) => `
|
|
15880
|
-
- ${label}:
|
|
15881
|
-
${block(trimmedContent
|
|
15882
|
-
.split('\n')
|
|
15883
|
-
.map((line) => `- ${line}`)
|
|
15884
|
-
.join('\n'))}
|
|
15885
|
-
`);
|
|
15886
|
-
}
|
|
15887
|
-
|
|
15888
16040
|
/**
|
|
15889
16041
|
* USE SEARCH ENGINE commitment definition
|
|
15890
16042
|
*
|
|
@@ -16253,151 +16405,991 @@ class NotYetImplementedCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
16253
16405
|
return spaceTrim$1(`
|
|
16254
16406
|
# ${this.type}
|
|
16255
16407
|
|
|
16256
|
-
This commitment is not yet fully implemented.
|
|
16408
|
+
This commitment is not yet fully implemented.
|
|
16409
|
+
|
|
16410
|
+
## Key aspects
|
|
16411
|
+
|
|
16412
|
+
- Content is appended directly to the system message.
|
|
16413
|
+
- No special processing or validation is performed.
|
|
16414
|
+
- Behavior preserved until proper implementation is added.
|
|
16415
|
+
|
|
16416
|
+
## Status
|
|
16417
|
+
|
|
16418
|
+
- **Status:** Placeholder implementation
|
|
16419
|
+
- **Effect:** Appends content prefixed by commitment type
|
|
16420
|
+
- **Future:** Will be replaced with specialized logic
|
|
16421
|
+
|
|
16422
|
+
## Examples
|
|
16423
|
+
|
|
16424
|
+
\`\`\`book
|
|
16425
|
+
Example Agent
|
|
16426
|
+
|
|
16427
|
+
PERSONA You are a helpful assistant
|
|
16428
|
+
${this.type} Your content here
|
|
16429
|
+
RULE Always be helpful
|
|
16430
|
+
\`\`\`
|
|
16431
|
+
`);
|
|
16432
|
+
}
|
|
16433
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
16434
|
+
const trimmedContent = content.trim();
|
|
16435
|
+
if (!trimmedContent) {
|
|
16436
|
+
return requirements;
|
|
16437
|
+
}
|
|
16438
|
+
// Add the commitment content 1:1 to the system message
|
|
16439
|
+
const commitmentLine = `${this.type} ${trimmedContent}`;
|
|
16440
|
+
return this.appendToSystemMessage(requirements, commitmentLine, '\n\n');
|
|
16441
|
+
}
|
|
16442
|
+
}
|
|
16443
|
+
|
|
16444
|
+
/**
|
|
16445
|
+
* Registry of all available commitment definitions
|
|
16446
|
+
* This array contains instances of all commitment definitions
|
|
16447
|
+
* This is the single source of truth for all commitments in the system
|
|
16448
|
+
*
|
|
16449
|
+
* @private Use functions to access commitments instead of this array directly
|
|
16450
|
+
*/
|
|
16451
|
+
const COMMITMENT_REGISTRY = [
|
|
16452
|
+
// Fully implemented commitments
|
|
16453
|
+
new PersonaCommitmentDefinition('PERSONA'),
|
|
16454
|
+
new PersonaCommitmentDefinition('PERSONAE'),
|
|
16455
|
+
new KnowledgeCommitmentDefinition(),
|
|
16456
|
+
new MemoryCommitmentDefinition('MEMORY'),
|
|
16457
|
+
new MemoryCommitmentDefinition('MEMORIES'),
|
|
16458
|
+
new StyleCommitmentDefinition('STYLE'),
|
|
16459
|
+
new StyleCommitmentDefinition('STYLES'),
|
|
16460
|
+
new RuleCommitmentDefinition('RULES'),
|
|
16461
|
+
new RuleCommitmentDefinition('RULE'),
|
|
16462
|
+
new LanguageCommitmentDefinition('LANGUAGES'),
|
|
16463
|
+
new LanguageCommitmentDefinition('LANGUAGE'),
|
|
16464
|
+
new SampleCommitmentDefinition('SAMPLE'),
|
|
16465
|
+
new SampleCommitmentDefinition('EXAMPLE'),
|
|
16466
|
+
new FormatCommitmentDefinition('FORMAT'),
|
|
16467
|
+
new FormatCommitmentDefinition('FORMATS'),
|
|
16468
|
+
new TemplateCommitmentDefinition('TEMPLATE'),
|
|
16469
|
+
new TemplateCommitmentDefinition('TEMPLATES'),
|
|
16470
|
+
new FromCommitmentDefinition('FROM'),
|
|
16471
|
+
new ImportCommitmentDefinition('IMPORT'),
|
|
16472
|
+
new ImportCommitmentDefinition('IMPORTS'),
|
|
16473
|
+
new ModelCommitmentDefinition('MODEL'),
|
|
16474
|
+
new ModelCommitmentDefinition('MODELS'),
|
|
16475
|
+
new ActionCommitmentDefinition('ACTION'),
|
|
16476
|
+
new ActionCommitmentDefinition('ACTIONS'),
|
|
16477
|
+
new ComponentCommitmentDefinition(),
|
|
16478
|
+
new MetaImageCommitmentDefinition(),
|
|
16479
|
+
new MetaColorCommitmentDefinition(),
|
|
16480
|
+
new MetaFontCommitmentDefinition(),
|
|
16481
|
+
new MetaLinkCommitmentDefinition(),
|
|
16482
|
+
new MetaCommitmentDefinition(),
|
|
16483
|
+
new NoteCommitmentDefinition('NOTE'),
|
|
16484
|
+
new NoteCommitmentDefinition('NOTES'),
|
|
16485
|
+
new NoteCommitmentDefinition('COMMENT'),
|
|
16486
|
+
new NoteCommitmentDefinition('NONCE'),
|
|
16487
|
+
new NoteCommitmentDefinition('TODO'),
|
|
16488
|
+
new GoalCommitmentDefinition('GOAL'),
|
|
16489
|
+
new GoalCommitmentDefinition('GOALS'),
|
|
16490
|
+
new InitialMessageCommitmentDefinition(),
|
|
16491
|
+
new UserMessageCommitmentDefinition(),
|
|
16492
|
+
new AgentMessageCommitmentDefinition(),
|
|
16493
|
+
new MessageCommitmentDefinition('MESSAGE'),
|
|
16494
|
+
new MessageCommitmentDefinition('MESSAGES'),
|
|
16495
|
+
new ScenarioCommitmentDefinition('SCENARIO'),
|
|
16496
|
+
new ScenarioCommitmentDefinition('SCENARIOS'),
|
|
16497
|
+
new DeleteCommitmentDefinition('DELETE'),
|
|
16498
|
+
new DeleteCommitmentDefinition('CANCEL'),
|
|
16499
|
+
new DeleteCommitmentDefinition('DISCARD'),
|
|
16500
|
+
new DeleteCommitmentDefinition('REMOVE'),
|
|
16501
|
+
new DictionaryCommitmentDefinition(),
|
|
16502
|
+
new OpenCommitmentDefinition(),
|
|
16503
|
+
new ClosedCommitmentDefinition(),
|
|
16504
|
+
new TeamCommitmentDefinition(),
|
|
16505
|
+
new UseBrowserCommitmentDefinition(),
|
|
16506
|
+
new UseSearchEngineCommitmentDefinition(),
|
|
16507
|
+
new UseTimeCommitmentDefinition(),
|
|
16508
|
+
new UseEmailCommitmentDefinition(),
|
|
16509
|
+
new UseImageGeneratorCommitmentDefinition('USE IMAGE GENERATOR'),
|
|
16510
|
+
new UseImageGeneratorCommitmentDefinition('USE IMAGE GENERATION' /* <- TODO: Remove any */),
|
|
16511
|
+
new UseImageGeneratorCommitmentDefinition('IMAGE GENERATOR' /* <- TODO: Remove any */),
|
|
16512
|
+
new UseImageGeneratorCommitmentDefinition('IMAGE GENERATION' /* <- TODO: Remove any */),
|
|
16513
|
+
new UseImageGeneratorCommitmentDefinition('USE IMAGE' /* <- TODO: Remove any */),
|
|
16514
|
+
// <- Note: [⛹️] How to deal with commitment aliases with defined functions
|
|
16515
|
+
new UseMcpCommitmentDefinition(),
|
|
16516
|
+
new UseCommitmentDefinition(),
|
|
16517
|
+
// Not yet implemented commitments (using placeholder)
|
|
16518
|
+
new NotYetImplementedCommitmentDefinition('EXPECT'),
|
|
16519
|
+
new NotYetImplementedCommitmentDefinition('BEHAVIOUR'),
|
|
16520
|
+
new NotYetImplementedCommitmentDefinition('BEHAVIOURS'),
|
|
16521
|
+
new NotYetImplementedCommitmentDefinition('AVOID'),
|
|
16522
|
+
new NotYetImplementedCommitmentDefinition('AVOIDANCE'),
|
|
16523
|
+
new NotYetImplementedCommitmentDefinition('CONTEXT'),
|
|
16524
|
+
// <- TODO: Prompt: Leverage aliases instead of duplicating commitment definitions
|
|
16525
|
+
];
|
|
16526
|
+
/**
|
|
16527
|
+
* TODO: [🧠] Maybe create through standardized $register
|
|
16528
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
16529
|
+
*/
|
|
16530
|
+
|
|
16531
|
+
/**
|
|
16532
|
+
* Gets all available commitment definitions
|
|
16533
|
+
* @returns Array of all commitment definitions
|
|
16534
|
+
*
|
|
16535
|
+
* @public exported from `@promptbook/core`
|
|
16536
|
+
*/
|
|
16537
|
+
function getAllCommitmentDefinitions() {
|
|
16538
|
+
return $deepFreeze([...COMMITMENT_REGISTRY]);
|
|
16539
|
+
}
|
|
16540
|
+
|
|
16541
|
+
/**
|
|
16542
|
+
* Gets all function implementations provided by all commitments
|
|
16543
|
+
*
|
|
16544
|
+
* Note: This function is intended for browser use, there is also equivalent `getAllCommitmentsToolFunctionsForNode` for server use
|
|
16545
|
+
*
|
|
16546
|
+
* @public exported from `@promptbook/browser`
|
|
16547
|
+
*/
|
|
16548
|
+
function getAllCommitmentsToolFunctionsForBrowser() {
|
|
16549
|
+
const allToolFunctions = {};
|
|
16550
|
+
for (const commitmentDefinition of getAllCommitmentDefinitions()) {
|
|
16551
|
+
const toolFunctions = commitmentDefinition.getToolFunctions();
|
|
16552
|
+
for (const [funcName, funcImpl] of Object.entries(toolFunctions)) {
|
|
16553
|
+
if (allToolFunctions[funcName] !== undefined &&
|
|
16554
|
+
just(false) /* <- Note: [⛹️] How to deal with commitment aliases */) {
|
|
16555
|
+
throw new UnexpectedError(`Duplicate tool function name detected: \`${funcName}\` provided by commitment \`${commitmentDefinition.type}\``);
|
|
16556
|
+
}
|
|
16557
|
+
allToolFunctions[funcName] = funcImpl;
|
|
16558
|
+
}
|
|
16559
|
+
}
|
|
16560
|
+
return allToolFunctions;
|
|
16561
|
+
}
|
|
16562
|
+
|
|
16563
|
+
/**
|
|
16564
|
+
* Gets all function implementations provided by all commitments
|
|
16565
|
+
*
|
|
16566
|
+
* Note: This function is intended for server use, there is also equivalent `getAllCommitmentsToolFunctionsForBrowser` for browser use
|
|
16567
|
+
*
|
|
16568
|
+
* @public exported from `@promptbook/node`
|
|
16569
|
+
*/
|
|
16570
|
+
function getAllCommitmentsToolFunctionsForNode() {
|
|
16571
|
+
if (!$isRunningInNode()) {
|
|
16572
|
+
throw new EnvironmentMismatchError(spaceTrim(`
|
|
16573
|
+
Function getAllCommitmentsToolFunctionsForNode should be run in Node.js environment.
|
|
16574
|
+
|
|
16575
|
+
- In browser use getAllCommitmentsToolFunctionsForBrowser instead.
|
|
16576
|
+
- This function can include server-only tools which cannot run in browser environment.
|
|
16577
|
+
|
|
16578
|
+
`));
|
|
16579
|
+
}
|
|
16580
|
+
const allToolFunctionsInBrowser = getAllCommitmentsToolFunctionsForBrowser();
|
|
16581
|
+
const allToolFunctionsInNode = {
|
|
16582
|
+
/**
|
|
16583
|
+
* @@@
|
|
16584
|
+
*
|
|
16585
|
+
* Note: [🛺] This function has implementation both for browser and node, this is the full one for node
|
|
16586
|
+
*/
|
|
16587
|
+
async fetch_url_content(args) {
|
|
16588
|
+
console.log('!!!! [Tool] fetch_url_content called', { args });
|
|
16589
|
+
const { url } = args;
|
|
16590
|
+
return await fetchUrlContent(url);
|
|
16591
|
+
},
|
|
16592
|
+
// TODO: !!!! Unhardcode, make proper server function register from definitions
|
|
16593
|
+
};
|
|
16594
|
+
return { ...allToolFunctionsInBrowser, ...allToolFunctionsInNode };
|
|
16595
|
+
}
|
|
16596
|
+
/**
|
|
16597
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16598
|
+
* TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
|
|
16599
|
+
*/
|
|
16600
|
+
|
|
16601
|
+
/**
|
|
16602
|
+
* Normalize options for `execCommand` and `execCommands`
|
|
16603
|
+
*
|
|
16604
|
+
* Note: `$` is used to indicate that this function behaves differently according to `process.platform`
|
|
16605
|
+
*
|
|
16606
|
+
* @private internal utility of `execCommand` and `execCommands`
|
|
16607
|
+
*/
|
|
16608
|
+
function $execCommandNormalizeOptions(options) {
|
|
16609
|
+
var _a, _b, _c, _d;
|
|
16610
|
+
let command;
|
|
16611
|
+
let cwd;
|
|
16612
|
+
let crashOnError;
|
|
16613
|
+
let args = [];
|
|
16614
|
+
let timeout;
|
|
16615
|
+
let isVerbose;
|
|
16616
|
+
let env;
|
|
16617
|
+
if (typeof options === 'string') {
|
|
16618
|
+
// TODO: [1] DRY default values
|
|
16619
|
+
command = options;
|
|
16620
|
+
cwd = process.cwd();
|
|
16621
|
+
crashOnError = true;
|
|
16622
|
+
timeout = Infinity; // <- TODO: [⏳]
|
|
16623
|
+
isVerbose = DEFAULT_IS_VERBOSE;
|
|
16624
|
+
env = undefined;
|
|
16625
|
+
}
|
|
16626
|
+
else {
|
|
16627
|
+
/*
|
|
16628
|
+
TODO:
|
|
16629
|
+
if ((options as any).commands !== undefined) {
|
|
16630
|
+
commands = (options as any).commands;
|
|
16631
|
+
} else {
|
|
16632
|
+
commands = [(options as any).command];
|
|
16633
|
+
}
|
|
16634
|
+
*/
|
|
16635
|
+
// TODO: [1] DRY default values
|
|
16636
|
+
command = options.command;
|
|
16637
|
+
cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
|
|
16638
|
+
crashOnError = (_b = options.crashOnError) !== null && _b !== void 0 ? _b : true;
|
|
16639
|
+
timeout = (_c = options.timeout) !== null && _c !== void 0 ? _c : Infinity;
|
|
16640
|
+
isVerbose = (_d = options.isVerbose) !== null && _d !== void 0 ? _d : DEFAULT_IS_VERBOSE;
|
|
16641
|
+
env = options.env;
|
|
16642
|
+
}
|
|
16643
|
+
// TODO: /(-[a-zA-Z0-9-]+\s+[^\s]*)|[^\s]*/g
|
|
16644
|
+
const _ = Array.from(command.matchAll(/(".*")|([^\s]*)/g))
|
|
16645
|
+
.map(([match]) => match)
|
|
16646
|
+
.filter((arg) => arg !== '');
|
|
16647
|
+
if (_.length > 1) {
|
|
16648
|
+
[command, ...args] = _;
|
|
16649
|
+
}
|
|
16650
|
+
if (options.args) {
|
|
16651
|
+
args = [...args, ...options.args];
|
|
16652
|
+
}
|
|
16653
|
+
let humanReadableCommand = !['npx', 'npm'].includes(command) ? command : args[0];
|
|
16654
|
+
if (['ts-node'].includes(humanReadableCommand)) {
|
|
16655
|
+
humanReadableCommand += ` ${args[1]}`;
|
|
16656
|
+
}
|
|
16657
|
+
if (/^win/.test(process.platform) && ['npm', 'npx'].includes(command)) {
|
|
16658
|
+
command = `${command}.cmd`;
|
|
16659
|
+
}
|
|
16660
|
+
return { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose, env };
|
|
16661
|
+
}
|
|
16662
|
+
// TODO: This should show type error> execCommandNormalizeOptions({ command: '', commands: [''] });
|
|
16663
|
+
|
|
16664
|
+
/**
|
|
16665
|
+
* Run one command in a shell
|
|
16666
|
+
*
|
|
16667
|
+
*
|
|
16668
|
+
* Note: There are 2 similar functions in the codebase:
|
|
16669
|
+
* - `$execCommand` which runs a single command
|
|
16670
|
+
* - `$execCommands` which runs multiple commands
|
|
16671
|
+
* Note: `$` is used to indicate that this function is not a pure function - it runs a command in a shell
|
|
16672
|
+
*
|
|
16673
|
+
* @public exported from `@promptbook/node`
|
|
16674
|
+
*/
|
|
16675
|
+
function $execCommand(options) {
|
|
16676
|
+
if (!$isRunningInNode()) {
|
|
16677
|
+
throw new EnvironmentMismatchError('Function `$execCommand` can run only in Node environment.js');
|
|
16678
|
+
}
|
|
16679
|
+
return new Promise((resolve, reject) => {
|
|
16680
|
+
// eslint-disable-next-line prefer-const
|
|
16681
|
+
const { command, humanReadableCommand, args, cwd, crashOnError, timeout, isVerbose = DEFAULT_IS_VERBOSE, env, } = $execCommandNormalizeOptions(options);
|
|
16682
|
+
if (timeout !== Infinity) {
|
|
16683
|
+
// TODO: In waitasecond forTime(Infinity) should be equivalent to forEver()
|
|
16684
|
+
forTime(timeout).then(() => {
|
|
16685
|
+
if (crashOnError) {
|
|
16686
|
+
reject(new Error(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms`));
|
|
16687
|
+
}
|
|
16688
|
+
else {
|
|
16689
|
+
console.warn(`Command "${humanReadableCommand}" exceeded time limit of ${timeout}ms but continues running`);
|
|
16690
|
+
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
16691
|
+
resolve('Command exceeded time limit');
|
|
16692
|
+
}
|
|
16693
|
+
});
|
|
16694
|
+
}
|
|
16695
|
+
if (isVerbose) {
|
|
16696
|
+
console.info(colors.yellow(cwd) + ' ' + colors.green(command) + ' ' + colors.blue(args.join(' ')));
|
|
16697
|
+
}
|
|
16698
|
+
try {
|
|
16699
|
+
const commandProcess = spawn(command, args, {
|
|
16700
|
+
cwd,
|
|
16701
|
+
shell: true,
|
|
16702
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
16703
|
+
});
|
|
16704
|
+
if (isVerbose) {
|
|
16705
|
+
commandProcess.on('message', (message) => {
|
|
16706
|
+
console.info({ message });
|
|
16707
|
+
});
|
|
16708
|
+
}
|
|
16709
|
+
const output = [];
|
|
16710
|
+
commandProcess.stdout.on('data', (stdout) => {
|
|
16711
|
+
output.push(stdout.toString());
|
|
16712
|
+
if (isVerbose) {
|
|
16713
|
+
console.info(stdout.toString());
|
|
16714
|
+
}
|
|
16715
|
+
});
|
|
16716
|
+
commandProcess.stderr.on('data', (stderr) => {
|
|
16717
|
+
output.push(stderr.toString());
|
|
16718
|
+
if (isVerbose && stderr.toString().trim()) {
|
|
16719
|
+
console.warn(stderr.toString());
|
|
16720
|
+
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
16721
|
+
}
|
|
16722
|
+
});
|
|
16723
|
+
const finishWithCode = (code) => {
|
|
16724
|
+
if (code !== 0) {
|
|
16725
|
+
if (crashOnError) {
|
|
16726
|
+
reject(new Error(output.join('\n').trim() ||
|
|
16727
|
+
`Command "${humanReadableCommand}" exited with code ${code}`));
|
|
16728
|
+
}
|
|
16729
|
+
else {
|
|
16730
|
+
if (isVerbose) {
|
|
16731
|
+
console.warn(`Command "${humanReadableCommand}" exited with code ${code}`);
|
|
16732
|
+
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
16733
|
+
}
|
|
16734
|
+
resolve(spaceTrim$1(output.join('\n')));
|
|
16735
|
+
}
|
|
16736
|
+
}
|
|
16737
|
+
else {
|
|
16738
|
+
resolve(spaceTrim$1(output.join('\n')));
|
|
16739
|
+
}
|
|
16740
|
+
};
|
|
16741
|
+
commandProcess.on('close', finishWithCode);
|
|
16742
|
+
commandProcess.on('exit', finishWithCode);
|
|
16743
|
+
commandProcess.on('disconnect', () => {
|
|
16744
|
+
// Note: Unexpected disconnection should always result in rejection
|
|
16745
|
+
reject(new Error(`Command "${humanReadableCommand}" disconnected`));
|
|
16746
|
+
});
|
|
16747
|
+
commandProcess.on('error', (error) => {
|
|
16748
|
+
if (crashOnError) {
|
|
16749
|
+
reject(new Error(`Command "${humanReadableCommand}" failed: \n${error.message}`));
|
|
16750
|
+
}
|
|
16751
|
+
else {
|
|
16752
|
+
if (isVerbose) {
|
|
16753
|
+
console.warn(error);
|
|
16754
|
+
// <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
|
|
16755
|
+
}
|
|
16756
|
+
resolve(spaceTrim$1(output.join('\n')));
|
|
16757
|
+
}
|
|
16758
|
+
});
|
|
16759
|
+
}
|
|
16760
|
+
catch (error) {
|
|
16761
|
+
// Note: Unexpected error in sync code should always result in rejection
|
|
16762
|
+
reject(error);
|
|
16763
|
+
}
|
|
16764
|
+
});
|
|
16765
|
+
}
|
|
16766
|
+
/**
|
|
16767
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16768
|
+
*/
|
|
16769
|
+
|
|
16770
|
+
/**
|
|
16771
|
+
* Attempts to locate the specified application on a Linux system using the 'which' command.
|
|
16772
|
+
* Returns the path to the executable if found, or null otherwise.
|
|
16773
|
+
*
|
|
16774
|
+
* @private within the repository
|
|
16775
|
+
*/
|
|
16776
|
+
async function locateAppOnLinux({ linuxWhich, }) {
|
|
16777
|
+
try {
|
|
16778
|
+
const result = await $execCommand({ crashOnError: true, command: `which ${linuxWhich}` });
|
|
16779
|
+
return result.trim();
|
|
16780
|
+
}
|
|
16781
|
+
catch (error) {
|
|
16782
|
+
assertsError(error);
|
|
16783
|
+
return null;
|
|
16784
|
+
}
|
|
16785
|
+
}
|
|
16786
|
+
/**
|
|
16787
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
16788
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16789
|
+
*/
|
|
16790
|
+
|
|
16791
|
+
/**
|
|
16792
|
+
* Provides filesystem access (for example for Node.js-based scrapers)
|
|
16793
|
+
* Creates a standardized filesystem interface that scrapers can use for file operations.
|
|
16794
|
+
*
|
|
16795
|
+
* @public exported from `@promptbook/node`
|
|
16796
|
+
*/
|
|
16797
|
+
function $provideFilesystemForNode(options) {
|
|
16798
|
+
if (!$isRunningInNode()) {
|
|
16799
|
+
throw new EnvironmentMismatchError('Function `$provideFilesystemForNode` works only in Node.js environment');
|
|
16800
|
+
}
|
|
16801
|
+
return {
|
|
16802
|
+
stat,
|
|
16803
|
+
access,
|
|
16804
|
+
constants,
|
|
16805
|
+
readFile,
|
|
16806
|
+
writeFile,
|
|
16807
|
+
readdir,
|
|
16808
|
+
mkdir,
|
|
16809
|
+
watch,
|
|
16810
|
+
};
|
|
16811
|
+
}
|
|
16812
|
+
/**
|
|
16813
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16814
|
+
* TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
|
|
16815
|
+
*/
|
|
16816
|
+
|
|
16817
|
+
/**
|
|
16818
|
+
* Checks if the file is executable
|
|
16819
|
+
*
|
|
16820
|
+
* @private within the repository
|
|
16821
|
+
*/
|
|
16822
|
+
async function isExecutable(path, fs) {
|
|
16823
|
+
try {
|
|
16824
|
+
await fs.access(path, fs.constants.X_OK);
|
|
16825
|
+
return true;
|
|
16826
|
+
}
|
|
16827
|
+
catch (error) {
|
|
16828
|
+
return false;
|
|
16829
|
+
}
|
|
16830
|
+
}
|
|
16831
|
+
/**
|
|
16832
|
+
* Note: Not [~🟢~] because it is not directly dependent on `fs
|
|
16833
|
+
* TODO: [🖇] What about symlinks?
|
|
16834
|
+
*/
|
|
16835
|
+
|
|
16836
|
+
// Note: Module `userhome` has no types available, so it is imported using `require`
|
|
16837
|
+
// @see https://stackoverflow.com/questions/37000981/how-to-import-node-module-in-typescript-without-type-definitions
|
|
16838
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
16839
|
+
const userhome = require('userhome');
|
|
16840
|
+
/**
|
|
16841
|
+
* Attempts to locate the specified application on a macOS system by checking standard application paths and using mdfind.
|
|
16842
|
+
* Returns the path to the executable if found, or null otherwise.
|
|
16843
|
+
*
|
|
16844
|
+
* @private within the repository
|
|
16845
|
+
*/
|
|
16846
|
+
async function locateAppOnMacOs({ macOsName, }) {
|
|
16847
|
+
try {
|
|
16848
|
+
const toExec = `/Contents/MacOS/${macOsName}`;
|
|
16849
|
+
const regPath = `/Applications/${macOsName}.app` + toExec;
|
|
16850
|
+
const altPath = userhome(regPath.slice(1));
|
|
16851
|
+
if (await isExecutable(regPath, $provideFilesystemForNode())) {
|
|
16852
|
+
return regPath;
|
|
16853
|
+
}
|
|
16854
|
+
else if (await isExecutable(altPath, $provideFilesystemForNode())) {
|
|
16855
|
+
return altPath;
|
|
16856
|
+
}
|
|
16857
|
+
const result = await $execCommand({
|
|
16858
|
+
crashOnError: true,
|
|
16859
|
+
command: `mdfind 'kMDItemDisplayName == "${macOsName}" && kMDItemKind == Application'`,
|
|
16860
|
+
});
|
|
16861
|
+
return result.trim() + toExec;
|
|
16862
|
+
}
|
|
16863
|
+
catch (error) {
|
|
16864
|
+
assertsError(error);
|
|
16865
|
+
return null;
|
|
16866
|
+
}
|
|
16867
|
+
}
|
|
16868
|
+
/**
|
|
16869
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
16870
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16871
|
+
*/
|
|
16872
|
+
|
|
16873
|
+
/**
|
|
16874
|
+
* Attempts to locate the specified application on a Windows system by searching common installation directories.
|
|
16875
|
+
* Returns the path to the executable if found, or null otherwise.
|
|
16876
|
+
*
|
|
16877
|
+
* @private within the repository
|
|
16878
|
+
*/
|
|
16879
|
+
async function locateAppOnWindows({ appName, windowsSuffix, }) {
|
|
16880
|
+
try {
|
|
16881
|
+
const prefixes = [
|
|
16882
|
+
process.env.LOCALAPPDATA,
|
|
16883
|
+
join(process.env.LOCALAPPDATA || '', 'Programs'),
|
|
16884
|
+
process.env.PROGRAMFILES,
|
|
16885
|
+
process.env['PROGRAMFILES(X86)'],
|
|
16886
|
+
];
|
|
16887
|
+
for (const prefix of prefixes) {
|
|
16888
|
+
const path = prefix + windowsSuffix;
|
|
16889
|
+
if (await isExecutable(path, $provideFilesystemForNode())) {
|
|
16890
|
+
return path;
|
|
16891
|
+
}
|
|
16892
|
+
}
|
|
16893
|
+
throw new Error(`Can not locate app ${appName} on Windows.`);
|
|
16894
|
+
}
|
|
16895
|
+
catch (error) {
|
|
16896
|
+
assertsError(error);
|
|
16897
|
+
return null;
|
|
16898
|
+
}
|
|
16899
|
+
}
|
|
16900
|
+
/**
|
|
16901
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
16902
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16903
|
+
*/
|
|
16904
|
+
|
|
16905
|
+
/**
|
|
16906
|
+
* Locates an application on the system
|
|
16907
|
+
*
|
|
16908
|
+
* @private within the repository
|
|
16909
|
+
*/
|
|
16910
|
+
function locateApp(options) {
|
|
16911
|
+
if (!$isRunningInNode()) {
|
|
16912
|
+
throw new EnvironmentMismatchError('Locating apps works only in Node.js environment');
|
|
16913
|
+
}
|
|
16914
|
+
const { appName, linuxWhich, windowsSuffix, macOsName } = options;
|
|
16915
|
+
if (process.platform === 'win32') {
|
|
16916
|
+
if (windowsSuffix) {
|
|
16917
|
+
return locateAppOnWindows({ appName, windowsSuffix });
|
|
16918
|
+
}
|
|
16919
|
+
else {
|
|
16920
|
+
throw new Error(`${appName} is not available on Windows.`);
|
|
16921
|
+
}
|
|
16922
|
+
}
|
|
16923
|
+
else if (process.platform === 'darwin') {
|
|
16924
|
+
if (macOsName) {
|
|
16925
|
+
return locateAppOnMacOs({ macOsName });
|
|
16926
|
+
}
|
|
16927
|
+
else {
|
|
16928
|
+
throw new Error(`${appName} is not available on macOS.`);
|
|
16929
|
+
}
|
|
16930
|
+
}
|
|
16931
|
+
else {
|
|
16932
|
+
if (linuxWhich) {
|
|
16933
|
+
return locateAppOnLinux({ linuxWhich });
|
|
16934
|
+
}
|
|
16935
|
+
else {
|
|
16936
|
+
throw new Error(`${appName} is not available on Linux.`);
|
|
16937
|
+
}
|
|
16938
|
+
}
|
|
16939
|
+
}
|
|
16940
|
+
/**
|
|
16941
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node`
|
|
16942
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16943
|
+
*/
|
|
16944
|
+
|
|
16945
|
+
/**
|
|
16946
|
+
* Locates the LibreOffice executable on the current system by searching platform-specific paths.
|
|
16947
|
+
* Returns the path to the executable if found, or null otherwise.
|
|
16948
|
+
*
|
|
16949
|
+
* @private within the repository
|
|
16950
|
+
*/
|
|
16951
|
+
function locateLibreoffice() {
|
|
16952
|
+
return locateApp({
|
|
16953
|
+
appName: 'Libreoffice',
|
|
16954
|
+
linuxWhich: 'libreoffice',
|
|
16955
|
+
windowsSuffix: '\\LibreOffice\\program\\soffice.exe',
|
|
16956
|
+
macOsName: 'LibreOffice',
|
|
16957
|
+
});
|
|
16958
|
+
}
|
|
16959
|
+
/**
|
|
16960
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node` OR `@promptbook/legacy-documents`
|
|
16961
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16962
|
+
*/
|
|
16963
|
+
|
|
16964
|
+
/**
|
|
16965
|
+
* Locates the Pandoc executable on the current system by searching platform-specific paths.
|
|
16966
|
+
* Returns the path to the executable if found, or null otherwise.
|
|
16967
|
+
*
|
|
16968
|
+
* @private within the repository
|
|
16969
|
+
*/
|
|
16970
|
+
function locatePandoc() {
|
|
16971
|
+
return locateApp({
|
|
16972
|
+
appName: 'Pandoc',
|
|
16973
|
+
linuxWhich: 'pandoc',
|
|
16974
|
+
windowsSuffix: '\\Pandoc\\pandoc.exe',
|
|
16975
|
+
macOsName: 'Pandoc',
|
|
16976
|
+
});
|
|
16977
|
+
}
|
|
16978
|
+
/**
|
|
16979
|
+
* TODO: [🧠][♿] Maybe export through `@promptbook/node` OR `@promptbook/documents`
|
|
16980
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
16981
|
+
*/
|
|
16982
|
+
|
|
16983
|
+
/**
|
|
16984
|
+
* Provides paths to required executables (i.e. as Pandoc and LibreOffice) for Node.js environments.
|
|
16985
|
+
*
|
|
16986
|
+
* @public exported from `@promptbook/node`
|
|
16987
|
+
*/
|
|
16988
|
+
async function $provideExecutablesForNode(options) {
|
|
16989
|
+
if (!$isRunningInNode()) {
|
|
16990
|
+
throw new EnvironmentMismatchError('Function `$getScrapersForNode` works only in Node.js environment');
|
|
16991
|
+
}
|
|
16992
|
+
return {
|
|
16993
|
+
pandocPath: (await locatePandoc()) || undefined,
|
|
16994
|
+
libreOfficePath: (await locateLibreoffice()) || undefined,
|
|
16995
|
+
// <- TODO: [🧠] `null` vs `undefined`
|
|
16996
|
+
};
|
|
16997
|
+
}
|
|
16998
|
+
/**
|
|
16999
|
+
* TODO: [🧠] Allow to override the executables without need to call `locatePandoc` / `locateLibreoffice` in case of provided
|
|
17000
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
17001
|
+
* TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
|
|
17002
|
+
*/
|
|
17003
|
+
|
|
17004
|
+
/**
|
|
17005
|
+
* Register for LLM tools metadata.
|
|
17006
|
+
*
|
|
17007
|
+
* Note: `$` is used to indicate that this interacts with the global scope
|
|
17008
|
+
* @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
|
|
17009
|
+
* @public exported from `@promptbook/core`
|
|
17010
|
+
*/
|
|
17011
|
+
const $llmToolsMetadataRegister = new $Register('llm_tools_metadata');
|
|
17012
|
+
/**
|
|
17013
|
+
* TODO: [®] DRY Register logic
|
|
17014
|
+
*/
|
|
17015
|
+
|
|
17016
|
+
/**
|
|
17017
|
+
* Register for LLM tools.
|
|
17018
|
+
*
|
|
17019
|
+
* Note: `$` is used to indicate that this interacts with the global scope
|
|
17020
|
+
* @singleton Only one instance of each register is created per build, but there can be more instances across different builds or environments.
|
|
17021
|
+
* @public exported from `@promptbook/core`
|
|
17022
|
+
*/
|
|
17023
|
+
const $llmToolsRegister = new $Register('llm_execution_tools_constructors');
|
|
17024
|
+
/**
|
|
17025
|
+
* TODO: [®] DRY Register logic
|
|
17026
|
+
*/
|
|
17027
|
+
|
|
17028
|
+
/**
|
|
17029
|
+
* Path to the `.env` file which was used to configure LLM tools
|
|
17030
|
+
*
|
|
17031
|
+
* Note: `$` is used to indicate that this variable is changed by side effect in `$provideLlmToolsConfigurationFromEnv` through `$setUsedEnvFilename`
|
|
17032
|
+
*/
|
|
17033
|
+
let $usedEnvFilename = null;
|
|
17034
|
+
/**
|
|
17035
|
+
* Pass the `.env` file which was used to configure LLM tools
|
|
17036
|
+
*
|
|
17037
|
+
* Note: `$` is used to indicate that this variable is making side effect
|
|
17038
|
+
*
|
|
17039
|
+
* @private internal log of `$provideLlmToolsConfigurationFromEnv` and `$registeredLlmToolsMessage`
|
|
17040
|
+
*/
|
|
17041
|
+
function $setUsedEnvFilename(filepath) {
|
|
17042
|
+
$usedEnvFilename = filepath;
|
|
17043
|
+
}
|
|
17044
|
+
/**
|
|
17045
|
+
* Creates a message with all registered LLM tools
|
|
17046
|
+
*
|
|
17047
|
+
* Note: This function is used to create a (error) message when there is no constructor for some LLM provider
|
|
17048
|
+
*
|
|
17049
|
+
* @private internal function of `createLlmToolsFromConfiguration` and `$provideLlmToolsFromEnv`
|
|
17050
|
+
*/
|
|
17051
|
+
function $registeredLlmToolsMessage() {
|
|
17052
|
+
let env;
|
|
17053
|
+
if ($isRunningInNode()) {
|
|
17054
|
+
env = process.env;
|
|
17055
|
+
// <- TODO: [⚛] Some DRY way how to get to `process.env` and pass it into functions - ACRY search for `env`
|
|
17056
|
+
}
|
|
17057
|
+
else {
|
|
17058
|
+
env = {};
|
|
17059
|
+
}
|
|
17060
|
+
/**
|
|
17061
|
+
* Mixes registered LLM tools from $llmToolsMetadataRegister and $llmToolsRegister
|
|
17062
|
+
*/
|
|
17063
|
+
const all = [];
|
|
17064
|
+
for (const { title, packageName, className, envVariables } of $llmToolsMetadataRegister.list()) {
|
|
17065
|
+
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
17066
|
+
continue;
|
|
17067
|
+
}
|
|
17068
|
+
all.push({ title, packageName, className, envVariables });
|
|
17069
|
+
}
|
|
17070
|
+
for (const { packageName, className } of $llmToolsRegister.list()) {
|
|
17071
|
+
if (all.some((item) => item.packageName === packageName && item.className === className)) {
|
|
17072
|
+
continue;
|
|
17073
|
+
}
|
|
17074
|
+
all.push({ packageName, className });
|
|
17075
|
+
}
|
|
17076
|
+
const metadata = all.map((metadata) => {
|
|
17077
|
+
var _a, _b;
|
|
17078
|
+
const isMetadataAviailable = $llmToolsMetadataRegister
|
|
17079
|
+
.list()
|
|
17080
|
+
.some(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
|
|
17081
|
+
const isInstalled = $llmToolsRegister
|
|
17082
|
+
.list()
|
|
17083
|
+
.some(({ packageName, className }) => metadata.packageName === packageName && metadata.className === className);
|
|
17084
|
+
const isFullyConfigured = ((_a = metadata.envVariables) === null || _a === void 0 ? void 0 : _a.every((envVariableName) => env[envVariableName] !== undefined)) || false;
|
|
17085
|
+
const isPartiallyConfigured = ((_b = metadata.envVariables) === null || _b === void 0 ? void 0 : _b.some((envVariableName) => env[envVariableName] !== undefined)) || false;
|
|
17086
|
+
// <- Note: [🗨]
|
|
17087
|
+
return { ...metadata, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured };
|
|
17088
|
+
});
|
|
17089
|
+
const usedEnvMessage = $usedEnvFilename === null ? `Unknown \`.env\` file` : `Used \`.env\` file:\n${$usedEnvFilename}`;
|
|
17090
|
+
if (metadata.length === 0) {
|
|
17091
|
+
return spaceTrim$2((block) => `
|
|
17092
|
+
No LLM providers are available.
|
|
17093
|
+
|
|
17094
|
+
${block(usedEnvMessage)}
|
|
17095
|
+
`);
|
|
17096
|
+
}
|
|
17097
|
+
return spaceTrim$2((block) => `
|
|
17098
|
+
|
|
17099
|
+
${block(usedEnvMessage)}
|
|
17100
|
+
|
|
17101
|
+
Relevant environment variables:
|
|
17102
|
+
${block(Object.keys(env)
|
|
17103
|
+
.filter((envVariableName) => metadata.some(({ envVariables }) => envVariables === null || envVariables === void 0 ? void 0 : envVariables.includes(envVariableName)))
|
|
17104
|
+
.map((envVariableName) => `- \`${envVariableName}\``)
|
|
17105
|
+
.join('\n'))}
|
|
17106
|
+
|
|
17107
|
+
Available LLM providers are:
|
|
17108
|
+
${block(metadata
|
|
17109
|
+
.map(({ title, packageName, className, envVariables, isMetadataAviailable, isInstalled, isFullyConfigured, isPartiallyConfigured, }, i) => {
|
|
17110
|
+
const morePieces = [];
|
|
17111
|
+
if (just(false)) ;
|
|
17112
|
+
else if (!isMetadataAviailable && !isInstalled) {
|
|
17113
|
+
// TODO: [�][�] Maybe do allow to do auto-install if package not registered and not found
|
|
17114
|
+
morePieces.push(`Not installed and no metadata, looks like a unexpected behavior`);
|
|
17115
|
+
}
|
|
17116
|
+
else if (isMetadataAviailable && !isInstalled) {
|
|
17117
|
+
// TODO: [�][�]
|
|
17118
|
+
morePieces.push(`Not installed`);
|
|
17119
|
+
}
|
|
17120
|
+
else if (!isMetadataAviailable && isInstalled) {
|
|
17121
|
+
morePieces.push(`No metadata but installed, looks like a unexpected behavior`);
|
|
17122
|
+
}
|
|
17123
|
+
else if (isMetadataAviailable && isInstalled) {
|
|
17124
|
+
morePieces.push(`Installed`);
|
|
17125
|
+
}
|
|
17126
|
+
else {
|
|
17127
|
+
morePieces.push(`unknown state, looks like a unexpected behavior`);
|
|
17128
|
+
} /* not else */
|
|
17129
|
+
if (isFullyConfigured) {
|
|
17130
|
+
morePieces.push(`Configured`);
|
|
17131
|
+
}
|
|
17132
|
+
else if (isPartiallyConfigured) {
|
|
17133
|
+
morePieces.push(`Partially confugured, missing ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.filter((envVariable) => env[envVariable] === undefined).join(' + ')}`);
|
|
17134
|
+
}
|
|
17135
|
+
else {
|
|
17136
|
+
if (envVariables !== null) {
|
|
17137
|
+
morePieces.push(`Not configured, to configure set env ${envVariables === null || envVariables === void 0 ? void 0 : envVariables.join(' + ')}`);
|
|
17138
|
+
}
|
|
17139
|
+
else {
|
|
17140
|
+
morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
|
|
17141
|
+
}
|
|
17142
|
+
}
|
|
17143
|
+
let providerMessage = spaceTrim$2(`
|
|
17144
|
+
${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
|
|
17145
|
+
${morePieces.join('; ')}
|
|
17146
|
+
`);
|
|
17147
|
+
if ($isRunningInNode()) {
|
|
17148
|
+
if (isInstalled && isFullyConfigured) {
|
|
17149
|
+
providerMessage = colors.green(providerMessage);
|
|
17150
|
+
}
|
|
17151
|
+
else if (isInstalled && isPartiallyConfigured) {
|
|
17152
|
+
providerMessage = colors.yellow(providerMessage);
|
|
17153
|
+
}
|
|
17154
|
+
else {
|
|
17155
|
+
providerMessage = colors.gray(providerMessage);
|
|
17156
|
+
}
|
|
17157
|
+
}
|
|
17158
|
+
return providerMessage;
|
|
17159
|
+
})
|
|
17160
|
+
.join('\n'))}
|
|
17161
|
+
`);
|
|
17162
|
+
}
|
|
17163
|
+
/**
|
|
17164
|
+
* TODO: [®] DRY Register logic
|
|
17165
|
+
* TODO: [🧠][⚛] Maybe pass env as argument
|
|
17166
|
+
*/
|
|
17167
|
+
|
|
17168
|
+
/**
|
|
17169
|
+
* Provides the path to the `.env` file
|
|
17170
|
+
*
|
|
17171
|
+
* Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
|
|
17172
|
+
*
|
|
17173
|
+
* @private within the repository - for CLI utils
|
|
17174
|
+
*/
|
|
17175
|
+
async function $provideEnvFilename() {
|
|
17176
|
+
if (!$isRunningInNode()) {
|
|
17177
|
+
throw new EnvironmentMismatchError('Function `$provideEnvFilename` works only in Node.js environment');
|
|
17178
|
+
}
|
|
17179
|
+
const envFilePatterns = [
|
|
17180
|
+
'.env',
|
|
17181
|
+
'.env.test',
|
|
17182
|
+
'.env.local',
|
|
17183
|
+
'.env.development.local',
|
|
17184
|
+
'.env.development',
|
|
17185
|
+
'.env.production.local',
|
|
17186
|
+
'.env.production',
|
|
17187
|
+
'.env.prod.local',
|
|
17188
|
+
'.env.prod',
|
|
17189
|
+
// <- TODO: Maybe add more patterns
|
|
17190
|
+
];
|
|
17191
|
+
let rootDirname = process.cwd();
|
|
17192
|
+
up_to_root: for (let i = 0; i < LOOP_LIMIT; i++) {
|
|
17193
|
+
for (const pattern of envFilePatterns) {
|
|
17194
|
+
const envFilename = join(rootDirname, pattern);
|
|
17195
|
+
if (await isFileExisting(envFilename, $provideFilesystemForNode())) {
|
|
17196
|
+
$setUsedEnvFilename(envFilename);
|
|
17197
|
+
return envFilename;
|
|
17198
|
+
}
|
|
17199
|
+
}
|
|
17200
|
+
if (isRootPath(rootDirname)) {
|
|
17201
|
+
break up_to_root;
|
|
17202
|
+
}
|
|
17203
|
+
// Note: If the directory does not exist, try the parent directory
|
|
17204
|
+
rootDirname = join(rootDirname, '..');
|
|
17205
|
+
}
|
|
17206
|
+
return null;
|
|
17207
|
+
}
|
|
17208
|
+
/**
|
|
17209
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
17210
|
+
*/
|
|
17211
|
+
|
|
17212
|
+
/**
|
|
17213
|
+
* Provides LLM tools configuration by reading environment variables.
|
|
17214
|
+
*
|
|
17215
|
+
* Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
|
|
17216
|
+
*
|
|
17217
|
+
* It looks for environment variables:
|
|
17218
|
+
* - `process.env.OPENAI_API_KEY`
|
|
17219
|
+
* - `process.env.ANTHROPIC_CLAUDE_API_KEY`
|
|
17220
|
+
* - ...
|
|
17221
|
+
*
|
|
17222
|
+
* @see Environment variables documentation or .env file for required variables.
|
|
17223
|
+
* @returns A promise that resolves to the LLM tools configuration, or null if configuration is incomplete or missing.
|
|
17224
|
+
* @public exported from `@promptbook/node`
|
|
17225
|
+
*/
|
|
17226
|
+
async function $provideLlmToolsConfigurationFromEnv() {
|
|
17227
|
+
if (!$isRunningInNode()) {
|
|
17228
|
+
throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
|
|
17229
|
+
}
|
|
17230
|
+
const envFilepath = await $provideEnvFilename();
|
|
17231
|
+
if (envFilepath !== null) {
|
|
17232
|
+
dotenv.config({ path: envFilepath });
|
|
17233
|
+
}
|
|
17234
|
+
const llmToolsConfiguration = $llmToolsMetadataRegister
|
|
17235
|
+
.list()
|
|
17236
|
+
.map((metadata) => metadata.createConfigurationFromEnv(process.env))
|
|
17237
|
+
.filter((configuration) => configuration !== null);
|
|
17238
|
+
return llmToolsConfiguration;
|
|
17239
|
+
}
|
|
17240
|
+
/**
|
|
17241
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
17242
|
+
*/
|
|
17243
|
+
|
|
17244
|
+
/**
|
|
17245
|
+
* Creates LLM execution tools from provided configuration objects
|
|
17246
|
+
*
|
|
17247
|
+
* Instantiates and configures LLM tool instances for each configuration entry,
|
|
17248
|
+
* combining them into a unified interface via MultipleLlmExecutionTools.
|
|
17249
|
+
*
|
|
17250
|
+
* Note: This function is not cached, every call creates new instance of `MultipleLlmExecutionTools`
|
|
17251
|
+
*
|
|
17252
|
+
* @param configuration Array of LLM tool configurations to instantiate
|
|
17253
|
+
* @param options Additional options for configuring the LLM tools
|
|
17254
|
+
* @returns A unified interface combining all successfully instantiated LLM tools
|
|
17255
|
+
* @public exported from `@promptbook/core`
|
|
17256
|
+
*/
|
|
17257
|
+
function createLlmToolsFromConfiguration(configuration, options = {}) {
|
|
17258
|
+
const { title = 'LLM Tools from Configuration', isVerbose = DEFAULT_IS_VERBOSE, userId } = options;
|
|
17259
|
+
const llmTools = configuration.map((llmConfiguration) => {
|
|
17260
|
+
const registeredItem = $llmToolsRegister
|
|
17261
|
+
.list()
|
|
17262
|
+
.find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
|
|
17263
|
+
if (registeredItem === undefined) {
|
|
17264
|
+
// console.log('$llmToolsRegister.list()', $llmToolsRegister.list());
|
|
17265
|
+
throw new Error(spaceTrim$2((block) => `
|
|
17266
|
+
There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
|
|
17267
|
+
Running in ${!$isRunningInBrowser() ? '' : 'browser environment'}${!$isRunningInNode() ? '' : 'node environment'}${!$isRunningInWebWorker() ? '' : 'worker environment'}
|
|
16257
17268
|
|
|
16258
|
-
|
|
17269
|
+
You have probably forgotten install and import the provider package.
|
|
17270
|
+
To fix this issue, you can:
|
|
16259
17271
|
|
|
16260
|
-
|
|
16261
|
-
- No special processing or validation is performed.
|
|
16262
|
-
- Behavior preserved until proper implementation is added.
|
|
17272
|
+
Install:
|
|
16263
17273
|
|
|
16264
|
-
|
|
17274
|
+
> npm install ${llmConfiguration.packageName}
|
|
16265
17275
|
|
|
16266
|
-
|
|
16267
|
-
- **Effect:** Appends content prefixed by commitment type
|
|
16268
|
-
- **Future:** Will be replaced with specialized logic
|
|
17276
|
+
And import:
|
|
16269
17277
|
|
|
16270
|
-
|
|
17278
|
+
> import '${llmConfiguration.packageName}';
|
|
16271
17279
|
|
|
16272
|
-
\`\`\`book
|
|
16273
|
-
Example Agent
|
|
16274
17280
|
|
|
16275
|
-
|
|
16276
|
-
|
|
16277
|
-
RULE Always be helpful
|
|
16278
|
-
\`\`\`
|
|
16279
|
-
`);
|
|
16280
|
-
}
|
|
16281
|
-
applyToAgentModelRequirements(requirements, content) {
|
|
16282
|
-
const trimmedContent = content.trim();
|
|
16283
|
-
if (!trimmedContent) {
|
|
16284
|
-
return requirements;
|
|
17281
|
+
${block($registeredLlmToolsMessage())}
|
|
17282
|
+
`));
|
|
16285
17283
|
}
|
|
16286
|
-
|
|
16287
|
-
|
|
16288
|
-
|
|
16289
|
-
|
|
17284
|
+
return registeredItem({
|
|
17285
|
+
isVerbose,
|
|
17286
|
+
userId,
|
|
17287
|
+
...llmConfiguration.options,
|
|
17288
|
+
});
|
|
17289
|
+
});
|
|
17290
|
+
return joinLlmExecutionTools(title, ...llmTools);
|
|
16290
17291
|
}
|
|
16291
|
-
|
|
16292
17292
|
/**
|
|
16293
|
-
*
|
|
16294
|
-
*
|
|
16295
|
-
*
|
|
16296
|
-
*
|
|
16297
|
-
*
|
|
17293
|
+
* TODO: [🎌] Together with `createLlmToolsFromConfiguration` + 'EXECUTION_TOOLS_CLASSES' gets to `@promptbook/core` ALL model providers, make this more efficient
|
|
17294
|
+
* TODO: [🧠][🎌] Dynamically install required providers
|
|
17295
|
+
* TODO: We should implement an interactive configuration wizard that would:
|
|
17296
|
+
* 1. Detect which LLM providers are available in the environment
|
|
17297
|
+
* 2. Guide users through required configuration settings for each provider
|
|
17298
|
+
* 3. Allow testing connections before completing setup
|
|
17299
|
+
* 4. Generate appropriate configuration code for application integration
|
|
17300
|
+
* TODO: [🧠][🍛] Which name is better `createLlmToolsFromConfig` or `createLlmToolsFromConfiguration`?
|
|
17301
|
+
* TODO: [🧠] Is there some meaningfull way how to test this util
|
|
17302
|
+
* TODO: This should be maybe not under `_common` but under `utils`
|
|
17303
|
+
* TODO: [®] DRY Register logic
|
|
16298
17304
|
*/
|
|
16299
|
-
|
|
16300
|
-
// Fully implemented commitments
|
|
16301
|
-
new PersonaCommitmentDefinition('PERSONA'),
|
|
16302
|
-
new PersonaCommitmentDefinition('PERSONAE'),
|
|
16303
|
-
new KnowledgeCommitmentDefinition(),
|
|
16304
|
-
new MemoryCommitmentDefinition('MEMORY'),
|
|
16305
|
-
new MemoryCommitmentDefinition('MEMORIES'),
|
|
16306
|
-
new StyleCommitmentDefinition('STYLE'),
|
|
16307
|
-
new StyleCommitmentDefinition('STYLES'),
|
|
16308
|
-
new RuleCommitmentDefinition('RULES'),
|
|
16309
|
-
new RuleCommitmentDefinition('RULE'),
|
|
16310
|
-
new LanguageCommitmentDefinition('LANGUAGES'),
|
|
16311
|
-
new LanguageCommitmentDefinition('LANGUAGE'),
|
|
16312
|
-
new SampleCommitmentDefinition('SAMPLE'),
|
|
16313
|
-
new SampleCommitmentDefinition('EXAMPLE'),
|
|
16314
|
-
new FormatCommitmentDefinition('FORMAT'),
|
|
16315
|
-
new FormatCommitmentDefinition('FORMATS'),
|
|
16316
|
-
new FromCommitmentDefinition('FROM'),
|
|
16317
|
-
new ImportCommitmentDefinition('IMPORT'),
|
|
16318
|
-
new ImportCommitmentDefinition('IMPORTS'),
|
|
16319
|
-
new ModelCommitmentDefinition('MODEL'),
|
|
16320
|
-
new ModelCommitmentDefinition('MODELS'),
|
|
16321
|
-
new ActionCommitmentDefinition('ACTION'),
|
|
16322
|
-
new ActionCommitmentDefinition('ACTIONS'),
|
|
16323
|
-
new ComponentCommitmentDefinition(),
|
|
16324
|
-
new MetaImageCommitmentDefinition(),
|
|
16325
|
-
new MetaColorCommitmentDefinition(),
|
|
16326
|
-
new MetaFontCommitmentDefinition(),
|
|
16327
|
-
new MetaLinkCommitmentDefinition(),
|
|
16328
|
-
new MetaCommitmentDefinition(),
|
|
16329
|
-
new NoteCommitmentDefinition('NOTE'),
|
|
16330
|
-
new NoteCommitmentDefinition('NOTES'),
|
|
16331
|
-
new NoteCommitmentDefinition('COMMENT'),
|
|
16332
|
-
new NoteCommitmentDefinition('NONCE'),
|
|
16333
|
-
new NoteCommitmentDefinition('TODO'),
|
|
16334
|
-
new GoalCommitmentDefinition('GOAL'),
|
|
16335
|
-
new GoalCommitmentDefinition('GOALS'),
|
|
16336
|
-
new InitialMessageCommitmentDefinition(),
|
|
16337
|
-
new UserMessageCommitmentDefinition(),
|
|
16338
|
-
new AgentMessageCommitmentDefinition(),
|
|
16339
|
-
new MessageCommitmentDefinition('MESSAGE'),
|
|
16340
|
-
new MessageCommitmentDefinition('MESSAGES'),
|
|
16341
|
-
new ScenarioCommitmentDefinition('SCENARIO'),
|
|
16342
|
-
new ScenarioCommitmentDefinition('SCENARIOS'),
|
|
16343
|
-
new DeleteCommitmentDefinition('DELETE'),
|
|
16344
|
-
new DeleteCommitmentDefinition('CANCEL'),
|
|
16345
|
-
new DeleteCommitmentDefinition('DISCARD'),
|
|
16346
|
-
new DeleteCommitmentDefinition('REMOVE'),
|
|
16347
|
-
new DictionaryCommitmentDefinition(),
|
|
16348
|
-
new OpenCommitmentDefinition(),
|
|
16349
|
-
new ClosedCommitmentDefinition(),
|
|
16350
|
-
new TeamCommitmentDefinition(),
|
|
16351
|
-
new UseBrowserCommitmentDefinition(),
|
|
16352
|
-
new UseSearchEngineCommitmentDefinition(),
|
|
16353
|
-
new UseTimeCommitmentDefinition(),
|
|
16354
|
-
new UseImageGeneratorCommitmentDefinition('USE IMAGE GENERATOR'),
|
|
16355
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16356
|
-
new UseImageGeneratorCommitmentDefinition('USE IMAGE GENERATION'),
|
|
16357
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16358
|
-
new UseImageGeneratorCommitmentDefinition('IMAGE GENERATOR'),
|
|
16359
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16360
|
-
new UseImageGeneratorCommitmentDefinition('IMAGE GENERATION'),
|
|
16361
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16362
|
-
new UseImageGeneratorCommitmentDefinition('USE IMAGE'),
|
|
16363
|
-
new UseMcpCommitmentDefinition(),
|
|
16364
|
-
new UseCommitmentDefinition(),
|
|
16365
|
-
// Not yet implemented commitments (using placeholder)
|
|
16366
|
-
new NotYetImplementedCommitmentDefinition('EXPECT'),
|
|
16367
|
-
new NotYetImplementedCommitmentDefinition('BEHAVIOUR'),
|
|
16368
|
-
new NotYetImplementedCommitmentDefinition('BEHAVIOURS'),
|
|
16369
|
-
new NotYetImplementedCommitmentDefinition('AVOID'),
|
|
16370
|
-
new NotYetImplementedCommitmentDefinition('AVOIDANCE'),
|
|
16371
|
-
new NotYetImplementedCommitmentDefinition('CONTEXT'),
|
|
16372
|
-
// <- TODO: Prompt: Leverage aliases instead of duplicating commitment definitions
|
|
16373
|
-
];
|
|
17305
|
+
|
|
16374
17306
|
/**
|
|
16375
|
-
*
|
|
16376
|
-
* @returns Array of all commitment definitions
|
|
17307
|
+
* Automatically configures LLM tools from environment variables in Node.js
|
|
16377
17308
|
*
|
|
16378
|
-
*
|
|
17309
|
+
* This utility function detects available LLM providers based on environment variables
|
|
17310
|
+
* and creates properly configured LLM execution tools for each detected provider.
|
|
17311
|
+
*
|
|
17312
|
+
* Note: This function is not cached, every call creates new instance of `MultipleLlmExecutionTools`
|
|
17313
|
+
*
|
|
17314
|
+
* Supports environment variables from .env files when dotenv is configured
|
|
17315
|
+
* Note: `$` is used to indicate that this function is not a pure function - it uses filesystem to access `.env` file
|
|
17316
|
+
*
|
|
17317
|
+
* It looks for environment variables:
|
|
17318
|
+
* - `process.env.OPENAI_API_KEY`
|
|
17319
|
+
* - `process.env.ANTHROPIC_CLAUDE_API_KEY`
|
|
17320
|
+
* - ...
|
|
17321
|
+
*
|
|
17322
|
+
* @param options Configuration options for the LLM tools
|
|
17323
|
+
* @returns A unified interface containing all detected and configured LLM tools
|
|
17324
|
+
* @public exported from `@promptbook/node`
|
|
16379
17325
|
*/
|
|
16380
|
-
function
|
|
16381
|
-
|
|
17326
|
+
async function $provideLlmToolsFromEnv(options = {}) {
|
|
17327
|
+
if (!$isRunningInNode()) {
|
|
17328
|
+
throw new EnvironmentMismatchError('Function `$provideLlmToolsFromEnv` works only in Node.js environment');
|
|
17329
|
+
}
|
|
17330
|
+
const configuration = await $provideLlmToolsConfigurationFromEnv();
|
|
17331
|
+
if (configuration.length === 0) {
|
|
17332
|
+
if ($llmToolsMetadataRegister.list().length === 0) {
|
|
17333
|
+
throw new UnexpectedError(spaceTrim$2((block) => `
|
|
17334
|
+
No LLM tools registered, this is probably a bug in the Promptbook library
|
|
17335
|
+
|
|
17336
|
+
${block($registeredLlmToolsMessage())}}
|
|
17337
|
+
`));
|
|
17338
|
+
}
|
|
17339
|
+
// TODO: [🥃]
|
|
17340
|
+
throw new Error(spaceTrim$2((block) => `
|
|
17341
|
+
No LLM tools found in the environment
|
|
17342
|
+
|
|
17343
|
+
${block($registeredLlmToolsMessage())}}
|
|
17344
|
+
`));
|
|
17345
|
+
}
|
|
17346
|
+
return createLlmToolsFromConfiguration(configuration, options);
|
|
16382
17347
|
}
|
|
16383
17348
|
/**
|
|
16384
|
-
*
|
|
17349
|
+
* TODO: The architecture for LLM tools configuration consists of three key functions:
|
|
17350
|
+
* 1. `$provideLlmToolsFromEnv` - High-level function that detects available providers from env vars and returns ready-to-use LLM tools
|
|
17351
|
+
* 2. `$provideLlmToolsConfigurationFromEnv` - Middle layer that extracts configuration objects from environment variables
|
|
17352
|
+
* 3. `createLlmToolsFromConfiguration` - Low-level function that instantiates LLM tools from explicit configuration
|
|
16385
17353
|
*
|
|
16386
|
-
*
|
|
17354
|
+
* This layered approach allows flexibility in how tools are configured:
|
|
17355
|
+
* - Use $provideLlmToolsFromEnv for automatic detection and setup in Node.js environments
|
|
17356
|
+
* - Use $provideLlmToolsConfigurationFromEnv to extract config objects for modification before instantiation
|
|
17357
|
+
* - Use createLlmToolsFromConfiguration for explicit control over tool configurations
|
|
17358
|
+
*
|
|
17359
|
+
* TODO: [🧠][🍛] Which name is better `$provideLlmToolsFromEnv` or `$provideLlmToolsFromEnvironment`?
|
|
17360
|
+
* TODO: [🧠] Is there some meaningfull way how to test this util
|
|
17361
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
17362
|
+
* TODO: [🥃] Allow `ptbk make` without llm tools
|
|
17363
|
+
* TODO: This should be maybe not under `_common` but under `utils`
|
|
17364
|
+
* TODO: [®] DRY Register logic
|
|
16387
17365
|
*/
|
|
16388
|
-
|
|
16389
|
-
|
|
16390
|
-
|
|
16391
|
-
|
|
16392
|
-
|
|
16393
|
-
|
|
17366
|
+
|
|
17367
|
+
/**
|
|
17368
|
+
* Provides a collection of scrapers optimized for Node.js environment.
|
|
17369
|
+
* 1) `provideScrapersForNode` use as default
|
|
17370
|
+
* 2) `provideScrapersForBrowser` use in limited browser environment *
|
|
17371
|
+
* @public exported from `@promptbook/node`
|
|
17372
|
+
*/
|
|
17373
|
+
async function $provideScrapersForNode(tools, options) {
|
|
17374
|
+
if (!$isRunningInNode()) {
|
|
17375
|
+
throw new EnvironmentMismatchError('Function `$getScrapersForNode` works only in Node.js environment');
|
|
17376
|
+
}
|
|
17377
|
+
// TODO: [🔱] Do here auto-installation + auto-include of missing scrapers - use all from $scrapersMetadataRegister.list()
|
|
17378
|
+
// TODO: [🔱][🧠] What is the best strategy for auto-install - install them all?
|
|
17379
|
+
const scrapers = [];
|
|
17380
|
+
for (const scraperFactory of $scrapersRegister.list()) {
|
|
17381
|
+
const scraper = await scraperFactory(tools, options || {});
|
|
17382
|
+
if (scraper.metadata.packageName === '@promptbook/boilerplate' ||
|
|
17383
|
+
scraper.metadata.mimeTypes.some((mimeType) => mimeType.includes('DISABLED'))) {
|
|
17384
|
+
continue;
|
|
16394
17385
|
}
|
|
17386
|
+
scrapers.push(scraper);
|
|
16395
17387
|
}
|
|
16396
|
-
return
|
|
17388
|
+
return scrapers;
|
|
16397
17389
|
}
|
|
16398
17390
|
/**
|
|
16399
|
-
*
|
|
16400
|
-
*
|
|
17391
|
+
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
17392
|
+
* TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
|
|
16401
17393
|
*/
|
|
16402
17394
|
|
|
16403
17395
|
/**
|
|
@@ -16580,13 +17572,6 @@ class JavascriptEvalExecutionTools {
|
|
|
16580
17572
|
`const ${functionName} = buildinFunctions.${functionName};`)
|
|
16581
17573
|
.join('\n');
|
|
16582
17574
|
// TODO: DRY [🍯]
|
|
16583
|
-
const commitmentsFunctions = getAllCommitmentsToolFunctions();
|
|
16584
|
-
const commitmentsFunctionsStatement = Object.keys(commitmentsFunctions)
|
|
16585
|
-
.map((functionName) =>
|
|
16586
|
-
// Note: Custom functions are exposed to the current scope as variables
|
|
16587
|
-
`const ${functionName} = commitmentsFunctions.${functionName};`)
|
|
16588
|
-
.join('\n');
|
|
16589
|
-
// TODO: DRY [🍯]
|
|
16590
17575
|
const customFunctions = this.options.functions || {};
|
|
16591
17576
|
const customFunctionsStatement = Object.keys(customFunctions)
|
|
16592
17577
|
.map((functionName) =>
|
|
@@ -16600,10 +17585,6 @@ class JavascriptEvalExecutionTools {
|
|
|
16600
17585
|
// Build-in functions:
|
|
16601
17586
|
${block(buildinFunctionsStatement)}
|
|
16602
17587
|
|
|
16603
|
-
// Commitments functions:
|
|
16604
|
-
${block(commitmentsFunctionsStatement)}
|
|
16605
|
-
|
|
16606
|
-
|
|
16607
17588
|
// Custom functions:
|
|
16608
17589
|
${block(customFunctionsStatement || '// -- No custom functions --')}
|
|
16609
17590
|
|
|
@@ -16710,12 +17691,18 @@ async function $provideExecutionToolsForNode(options) {
|
|
|
16710
17691
|
fs,
|
|
16711
17692
|
executables,
|
|
16712
17693
|
scrapers: await $provideScrapersForNode({ fs, llm, executables }, options),
|
|
16713
|
-
script: [
|
|
17694
|
+
script: [
|
|
17695
|
+
new JavascriptExecutionTools({
|
|
17696
|
+
...options,
|
|
17697
|
+
functions: { ...getAllCommitmentsToolFunctionsForNode() },
|
|
17698
|
+
}),
|
|
17699
|
+
],
|
|
16714
17700
|
};
|
|
16715
17701
|
return tools;
|
|
16716
17702
|
}
|
|
16717
17703
|
/**
|
|
16718
17704
|
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
17705
|
+
* TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
|
|
16719
17706
|
*/
|
|
16720
17707
|
|
|
16721
17708
|
/**
|
|
@@ -17037,10 +18024,11 @@ async function $provideScriptingForNode(options) {
|
|
|
17037
18024
|
throw new EnvironmentMismatchError('Function `$provideScriptingForNode` works only in Node.js environment');
|
|
17038
18025
|
}
|
|
17039
18026
|
// TODO: [🔱] Do here auto-installation
|
|
17040
|
-
return [new JavascriptExecutionTools(options)];
|
|
18027
|
+
return [new JavascriptExecutionTools({ ...options, functions: { ...getAllCommitmentsToolFunctionsForNode() } })];
|
|
17041
18028
|
}
|
|
17042
18029
|
/**
|
|
17043
18030
|
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
18031
|
+
* TODO: [🏓] Unite `xxxForServer` and `xxxForNode` naming
|
|
17044
18032
|
*/
|
|
17045
18033
|
|
|
17046
18034
|
/**
|
|
@@ -17188,5 +18176,5 @@ async function $execCommands({ commands, cwd, crashOnError, }) {
|
|
|
17188
18176
|
* Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
|
|
17189
18177
|
*/
|
|
17190
18178
|
|
|
17191
|
-
export { $execCommand, $execCommands, $provideExecutablesForNode, $provideExecutionToolsForNode, $provideFilesystemForNode, $provideLlmToolsConfigurationFromEnv, $provideLlmToolsFromEnv, $provideScrapersForNode, $provideScriptingForNode, BOOK_LANGUAGE_VERSION, FileCacheStorage, PROMPTBOOK_ENGINE_VERSION, createPipelineCollectionFromDirectory };
|
|
18179
|
+
export { $execCommand, $execCommands, $provideExecutablesForNode, $provideExecutionToolsForNode, $provideFilesystemForNode, $provideLlmToolsConfigurationFromEnv, $provideLlmToolsFromEnv, $provideScrapersForNode, $provideScriptingForNode, BOOK_LANGUAGE_VERSION, FileCacheStorage, PROMPTBOOK_ENGINE_VERSION, createPipelineCollectionFromDirectory, getAllCommitmentsToolFunctionsForNode };
|
|
17192
18180
|
//# sourceMappingURL=index.es.js.map
|