@promptbook/fake-llm 0.105.0-15 → 0.105.0-17

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/umd/index.umd.js CHANGED
@@ -1,12 +1,13 @@
1
1
  (function (global, factory) {
2
2
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('spacetrim'), require('waitasecond'), require('crypto'), require('lorem-ipsum'), require('path'), require('crypto-js'), require('crypto-js/enc-hex'), require('moment')) :
3
3
  typeof define === 'function' && define.amd ? define(['exports', 'spacetrim', 'waitasecond', 'crypto', 'lorem-ipsum', 'path', 'crypto-js', 'crypto-js/enc-hex', 'moment'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-fake-llm"] = {}, global.spaceTrim$1, global.waitasecond, global.crypto, global.loremIpsum, null, null, null, global.moment));
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-fake-llm"] = {}, global.spaceTrim$1, global.waitasecond, global.crypto, global.loremIpsum, null, global.cryptoJs, global.hexEncoder, global.moment));
5
5
  })(this, (function (exports, spaceTrim$1, waitasecond, crypto, loremIpsum, path, cryptoJs, hexEncoder, moment) { 'use strict';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
9
9
  var spaceTrim__default = /*#__PURE__*/_interopDefaultLegacy(spaceTrim$1);
10
+ var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
10
11
  var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
11
12
 
12
13
  // ⚠️ WARNING: This code has been generated so that any manual changes will be overwritten
@@ -23,7 +24,7 @@
23
24
  * @generated
24
25
  * @see https://github.com/webgptorg/promptbook
25
26
  */
26
- const PROMPTBOOK_ENGINE_VERSION = '0.105.0-15';
27
+ const PROMPTBOOK_ENGINE_VERSION = '0.105.0-17';
27
28
  /**
28
29
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
29
30
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -2539,6 +2540,19 @@
2539
2540
  * TODO: Maybe split `ParseError` and `ApplyError`
2540
2541
  */
2541
2542
 
2543
+ /**
2544
+ * Error thrown when a fetch request fails
2545
+ *
2546
+ * @public exported from `@promptbook/core`
2547
+ */
2548
+ class PromptbookFetchError extends Error {
2549
+ constructor(message) {
2550
+ super(message);
2551
+ this.name = 'PromptbookFetchError';
2552
+ Object.setPrototypeOf(this, PromptbookFetchError.prototype);
2553
+ }
2554
+ }
2555
+
2542
2556
  /**
2543
2557
  * Index of all javascript errors
2544
2558
  *
@@ -2566,6 +2580,18 @@
2566
2580
  * Note: [💞] Ignore a discrepancy between file name and entity name
2567
2581
  */
2568
2582
 
2583
+ /**
2584
+ * Computes SHA-256 hash of the given object
2585
+ *
2586
+ * @public exported from `@promptbook/utils`
2587
+ */
2588
+ function computeHash(value) {
2589
+ return cryptoJs.SHA256(hexEncoder__default["default"].parse(spaceTrim__default["default"](valueToString(value)))).toString( /* hex */);
2590
+ }
2591
+ /**
2592
+ * TODO: [🥬][🥬] Use this ACRY
2593
+ */
2594
+
2569
2595
  /**
2570
2596
  * Makes first letter of a string uppercase
2571
2597
  *
@@ -5859,6 +5885,432 @@
5859
5885
  * [💞] Ignore a discrepancy between file name and entity name
5860
5886
  */
5861
5887
 
5888
+ const urlRegex = /https?:\/\/[^\s]+/gi;
5889
+ const trailingPunctuationRegex = /[),.;!?]+$/;
5890
+ const clauseSeparators = ['.', '?', '!', ';', ','];
5891
+ const conjunctionSeparators = [' and ', ' or '];
5892
+ /**
5893
+ * Parses TEAM commitment content into teammates with instructions.
5894
+ *
5895
+ * @private
5896
+ */
5897
+ function parseTeamCommitmentContent(content, options = {}) {
5898
+ const { strict = false } = options;
5899
+ const lines = content
5900
+ .split('\n')
5901
+ .map((line) => line.trim())
5902
+ .filter(Boolean);
5903
+ const teammates = [];
5904
+ const seenUrls = new Set();
5905
+ for (const line of lines) {
5906
+ const matches = Array.from(line.matchAll(urlRegex));
5907
+ if (matches.length === 0) {
5908
+ if (strict) {
5909
+ throw new Error(`TEAM commitment expects at least one agent URL, got: "${line}"`);
5910
+ }
5911
+ continue;
5912
+ }
5913
+ for (const [matchIndex, match] of matches.entries()) {
5914
+ const rawUrl = match[0] || '';
5915
+ const cleanedUrl = rawUrl.replace(trailingPunctuationRegex, '');
5916
+ if (!isValidAgentUrl(cleanedUrl)) {
5917
+ if (strict) {
5918
+ throw new Error(`Invalid agent URL in TEAM commitment: "${cleanedUrl}"`);
5919
+ }
5920
+ continue;
5921
+ }
5922
+ if (seenUrls.has(cleanedUrl)) {
5923
+ continue;
5924
+ }
5925
+ seenUrls.add(cleanedUrl);
5926
+ const instructionContext = extractInstructionContext(line, matches, matchIndex);
5927
+ const instructions = normalizeInstructionText(instructionContext);
5928
+ const label = createTeammateLabel(cleanedUrl);
5929
+ teammates.push({
5930
+ url: cleanedUrl,
5931
+ label,
5932
+ instructions,
5933
+ });
5934
+ }
5935
+ }
5936
+ return teammates;
5937
+ }
5938
+ function extractInstructionContext(line, matches, matchIndex) {
5939
+ var _a;
5940
+ const match = matches[matchIndex];
5941
+ if (!match || match.index === undefined) {
5942
+ return line.trim();
5943
+ }
5944
+ const rawUrl = match[0] || '';
5945
+ const matchStart = match.index;
5946
+ const matchEnd = matchStart + rawUrl.length;
5947
+ const previousMatch = matches[matchIndex - 1];
5948
+ const nextMatch = matches[matchIndex + 1];
5949
+ const previousEnd = previousMatch && previousMatch.index !== undefined ? previousMatch.index + (((_a = previousMatch[0]) === null || _a === void 0 ? void 0 : _a.length) || 0) : 0;
5950
+ const nextStart = nextMatch && nextMatch.index !== undefined ? nextMatch.index : line.length;
5951
+ const rawPrefix = line.slice(previousEnd, matchStart);
5952
+ const rawSuffix = line.slice(matchEnd, nextStart);
5953
+ const prefix = trimAfterLastDelimiter(rawPrefix);
5954
+ const suffix = trimBeforeLastDelimiter(rawSuffix);
5955
+ if (normalizeInstructionText(suffix)) {
5956
+ return suffix;
5957
+ }
5958
+ if (normalizeInstructionText(prefix)) {
5959
+ return prefix;
5960
+ }
5961
+ return `${prefix} ${suffix}`.trim();
5962
+ }
5963
+ function trimAfterLastDelimiter(text) {
5964
+ const match = findLastDelimiter(text);
5965
+ if (!match) {
5966
+ return text;
5967
+ }
5968
+ return text.slice(match.index + match.length);
5969
+ }
5970
+ function trimBeforeLastDelimiter(text) {
5971
+ const cleaned = text.replace(/^[,;:]\s*/g, '');
5972
+ const match = findLastDelimiter(cleaned);
5973
+ if (!match || match.index <= 0) {
5974
+ return cleaned;
5975
+ }
5976
+ return cleaned.slice(0, match.index);
5977
+ }
5978
+ function findLastDelimiter(text) {
5979
+ let bestIndex = -1;
5980
+ let bestLength = 0;
5981
+ for (const separator of clauseSeparators) {
5982
+ const index = text.lastIndexOf(separator);
5983
+ if (index > bestIndex) {
5984
+ bestIndex = index;
5985
+ bestLength = separator.length;
5986
+ }
5987
+ }
5988
+ const lowerText = text.toLowerCase();
5989
+ for (const separator of conjunctionSeparators) {
5990
+ const index = lowerText.lastIndexOf(separator);
5991
+ if (index > bestIndex) {
5992
+ bestIndex = index;
5993
+ bestLength = separator.length;
5994
+ }
5995
+ }
5996
+ if (bestIndex === -1) {
5997
+ return null;
5998
+ }
5999
+ return { index: bestIndex, length: bestLength };
6000
+ }
6001
+ function normalizeInstructionText(text) {
6002
+ if (!text) {
6003
+ return '';
6004
+ }
6005
+ const withoutUrls = text.replace(urlRegex, '');
6006
+ let normalized = normalizeWhitespaces(withoutUrls).trim();
6007
+ normalized = normalized.replace(/^[,;:]\s*/g, '');
6008
+ normalized = normalized.replace(/^(and|or|the|a|an)\s+/i, '');
6009
+ normalized = normalized.replace(/\s*[,;:]\s*$/g, '');
6010
+ normalized = normalized.replace(/\s+(and|or)\s*$/i, '');
6011
+ normalized = normalizeWhitespaces(normalized).trim();
6012
+ return normalized;
6013
+ }
6014
+ function createTeammateLabel(url) {
6015
+ try {
6016
+ const parsed = new URL(url);
6017
+ const pathParts = parsed.pathname.split('/').filter(Boolean);
6018
+ const lastPart = pathParts[pathParts.length - 1] || parsed.hostname;
6019
+ const decoded = decodeURIComponent(lastPart);
6020
+ const spaced = decoded.replace(/[-_]+/g, ' ').trim();
6021
+ if (!spaced) {
6022
+ return parsed.hostname;
6023
+ }
6024
+ return spaced
6025
+ .split(' ')
6026
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
6027
+ .join(' ');
6028
+ }
6029
+ catch (error) {
6030
+ return url;
6031
+ }
6032
+ }
6033
+ /**
6034
+ * Note: [💞] Ignore a discrepancy between file name and entity name
6035
+ */
6036
+
6037
+ /**
6038
+ * The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
6039
+ *
6040
+ * @public exported from `@promptbook/core`
6041
+ */
6042
+ const promptbookFetch = async (urlOrRequest, init) => {
6043
+ try {
6044
+ return await fetch(urlOrRequest, init);
6045
+ }
6046
+ catch (error) {
6047
+ assertsError(error);
6048
+ let url;
6049
+ if (typeof urlOrRequest === 'string') {
6050
+ url = urlOrRequest;
6051
+ }
6052
+ else if (urlOrRequest instanceof Request) {
6053
+ url = urlOrRequest.url;
6054
+ }
6055
+ throw new PromptbookFetchError(spaceTrim__default["default"]((block) => `
6056
+ Can not fetch "${url}"
6057
+
6058
+ Fetch error:
6059
+ ${block(error.message)}
6060
+
6061
+ `));
6062
+ }
6063
+ };
6064
+ /**
6065
+ * TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
6066
+ */
6067
+
6068
+ const TEAM_TOOL_PREFIX = 'team_chat_';
6069
+ const teamToolFunctions = {};
6070
+ const teamToolTitles = {};
6071
+ /**
6072
+ * TEAM commitment definition
6073
+ *
6074
+ * The `TEAM` commitment defines teammates that the agent can consult via tools.
6075
+ *
6076
+ * Example usage in agent source:
6077
+ *
6078
+ * ```book
6079
+ * TEAM https://agents.ptbk.ik/agents/joe-green
6080
+ * TEAM You can talk with http://localhost:4440/agents/GMw67JN8TXxN7y to discuss the legal aspects.
6081
+ * ```
6082
+ *
6083
+ * @private [??] Maybe export the commitments through some package
6084
+ */
6085
+ class TeamCommitmentDefinition extends BaseCommitmentDefinition {
6086
+ constructor() {
6087
+ super('TEAM');
6088
+ }
6089
+ /**
6090
+ * Short one-line description of TEAM.
6091
+ */
6092
+ get description() {
6093
+ return 'Enable the agent to consult teammate agents via dedicated tools.';
6094
+ }
6095
+ /**
6096
+ * Icon for this commitment.
6097
+ */
6098
+ get icon() {
6099
+ return '??';
6100
+ }
6101
+ /**
6102
+ * Markdown documentation for TEAM commitment.
6103
+ */
6104
+ get documentation() {
6105
+ return spaceTrim$1.spaceTrim(`
6106
+ # TEAM
6107
+
6108
+ Registers teammate agents that the current agent can consult via tools.
6109
+
6110
+ ## Examples
6111
+
6112
+ \`\`\`book
6113
+ Legal Assistant
6114
+
6115
+ PERSONA An expert software developer
6116
+ TEAM You can talk with http://localhost:4440/agents/GMw67JN8TXxN7y to discuss the legal aspects.
6117
+ \`\`\`
6118
+ `);
6119
+ }
6120
+ applyToAgentModelRequirements(requirements, content) {
6121
+ var _a, _b;
6122
+ const trimmedContent = content.trim();
6123
+ if (!trimmedContent) {
6124
+ return requirements;
6125
+ }
6126
+ const teammates = parseTeamCommitmentContent(trimmedContent, { strict: true });
6127
+ if (teammates.length === 0) {
6128
+ return requirements;
6129
+ }
6130
+ const agentName = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.agentName) || 'Agent';
6131
+ const teamEntries = teammates.map((teammate) => ({
6132
+ toolName: createTeamToolName(teammate.url),
6133
+ teammate,
6134
+ agentName,
6135
+ }));
6136
+ for (const entry of teamEntries) {
6137
+ registerTeamTool(entry);
6138
+ }
6139
+ const existingTools = requirements.tools || [];
6140
+ const updatedTools = [...existingTools];
6141
+ for (const entry of teamEntries) {
6142
+ if (updatedTools.some((tool) => tool.name === entry.toolName)) {
6143
+ continue;
6144
+ }
6145
+ const instructionSuffix = entry.teammate.instructions
6146
+ ? `Use when: ${entry.teammate.instructions}`
6147
+ : 'Use when their expertise is needed.';
6148
+ updatedTools.push({
6149
+ name: entry.toolName,
6150
+ description: spaceTrim$1.spaceTrim(`
6151
+ Consult teammate ${entry.teammate.label} (${entry.teammate.url}).
6152
+ ${instructionSuffix}
6153
+ `),
6154
+ parameters: {
6155
+ type: 'object',
6156
+ properties: {
6157
+ message: {
6158
+ type: 'string',
6159
+ description: 'Question or request to send to the teammate.',
6160
+ },
6161
+ context: {
6162
+ type: 'string',
6163
+ description: 'Optional background context for the teammate.',
6164
+ },
6165
+ },
6166
+ required: ['message'],
6167
+ },
6168
+ });
6169
+ }
6170
+ const existingTeammates = ((_b = requirements.metadata) === null || _b === void 0 ? void 0 : _b.teammates) || [];
6171
+ const updatedTeammates = [...existingTeammates];
6172
+ for (const entry of teamEntries) {
6173
+ if (updatedTeammates.some((existing) => existing.url === entry.teammate.url)) {
6174
+ continue;
6175
+ }
6176
+ updatedTeammates.push({
6177
+ url: entry.teammate.url,
6178
+ label: entry.teammate.label,
6179
+ instructions: entry.teammate.instructions || undefined,
6180
+ toolName: entry.toolName,
6181
+ });
6182
+ }
6183
+ const teamSystemMessage = spaceTrim$1.spaceTrim((block) => `
6184
+ Teammates:
6185
+ ${block(teamEntries
6186
+ .map((entry) => {
6187
+ const whenToConsult = entry.teammate.instructions || 'Use when their expertise is needed.';
6188
+ return spaceTrim$1.spaceTrim(() => `
6189
+ - ${entry.teammate.label} (${entry.teammate.url})
6190
+ - Tool: "${entry.toolName}"
6191
+ - When to consult: ${whenToConsult}
6192
+ `);
6193
+ })
6194
+ .join('\n'))}
6195
+ `);
6196
+ return this.appendToSystemMessage({
6197
+ ...requirements,
6198
+ tools: updatedTools,
6199
+ metadata: {
6200
+ ...requirements.metadata,
6201
+ teammates: updatedTeammates,
6202
+ },
6203
+ }, teamSystemMessage);
6204
+ }
6205
+ /**
6206
+ * Gets human-readable titles for tool functions provided by this commitment.
6207
+ */
6208
+ getToolTitles() {
6209
+ return { ...teamToolTitles };
6210
+ }
6211
+ /**
6212
+ * Gets tool function implementations for teammate tools.
6213
+ */
6214
+ getToolFunctions() {
6215
+ return { ...teamToolFunctions };
6216
+ }
6217
+ }
6218
+ function createTeamToolName(url) {
6219
+ const hash = computeHash(url).substring(0, 10);
6220
+ return `${TEAM_TOOL_PREFIX}${hash}`;
6221
+ }
6222
+ function registerTeamTool(entry) {
6223
+ teamToolFunctions[entry.toolName] = createTeamToolFunction(entry);
6224
+ teamToolTitles[entry.toolName] = `Consult ${entry.teammate.label}`;
6225
+ }
6226
+ function createTeamToolFunction(entry) {
6227
+ return async (args) => {
6228
+ const message = args.message || args.question || '';
6229
+ if (!message) {
6230
+ const result = {
6231
+ error: 'Message is required to contact teammate.',
6232
+ teammate: {
6233
+ url: entry.teammate.url,
6234
+ label: entry.teammate.label,
6235
+ instructions: entry.teammate.instructions,
6236
+ toolName: entry.toolName,
6237
+ },
6238
+ };
6239
+ return JSON.stringify(result);
6240
+ }
6241
+ const request = args.context ? `${message}\n\nContext:\n${args.context}` : message;
6242
+ let response = '';
6243
+ let error = null;
6244
+ try {
6245
+ response = await fetchTeammateResponse(entry.teammate.url, request);
6246
+ }
6247
+ catch (err) {
6248
+ error = err instanceof Error ? err.message : String(err);
6249
+ }
6250
+ const teammateReply = response || (error ? `Unable to reach teammate. Error: ${error}` : 'No response received.');
6251
+ const result = {
6252
+ teammate: {
6253
+ url: entry.teammate.url,
6254
+ label: entry.teammate.label,
6255
+ instructions: entry.teammate.instructions,
6256
+ toolName: entry.toolName,
6257
+ },
6258
+ request,
6259
+ response: teammateReply,
6260
+ error,
6261
+ conversation: [
6262
+ {
6263
+ sender: 'AGENT',
6264
+ name: entry.agentName,
6265
+ content: request,
6266
+ },
6267
+ {
6268
+ sender: 'TEAMMATE',
6269
+ name: entry.teammate.label,
6270
+ content: teammateReply,
6271
+ },
6272
+ ],
6273
+ };
6274
+ return JSON.stringify(result);
6275
+ };
6276
+ }
6277
+ async function fetchTeammateResponse(agentUrl, message) {
6278
+ const url = `${agentUrl.replace(/\/$/, '')}/api/chat`;
6279
+ const response = await promptbookFetch(url, {
6280
+ method: 'POST',
6281
+ headers: {
6282
+ 'Content-Type': 'application/json',
6283
+ },
6284
+ body: JSON.stringify({ message }),
6285
+ });
6286
+ if (!response.ok) {
6287
+ throw new Error(`Teammate request failed: ${response.status} ${response.statusText}`);
6288
+ }
6289
+ const rawText = await response.text();
6290
+ return stripToolCallLines(rawText).trim();
6291
+ }
6292
+ function stripToolCallLines(text) {
6293
+ const lines = text.replace(/\r\n/g, '\n').split('\n');
6294
+ return lines
6295
+ .filter((line) => {
6296
+ const trimmed = line.trim();
6297
+ if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
6298
+ return true;
6299
+ }
6300
+ try {
6301
+ const parsed = JSON.parse(trimmed);
6302
+ return !('toolCalls' in parsed);
6303
+ }
6304
+ catch (_a) {
6305
+ return true;
6306
+ }
6307
+ })
6308
+ .join('\n');
6309
+ }
6310
+ /**
6311
+ * Note: [💞] Ignore a discrepancy between file name and entity name
6312
+ */
6313
+
5862
6314
  /**
5863
6315
  * USE commitment definition
5864
6316
  *
@@ -6853,6 +7305,7 @@
6853
7305
  new DictionaryCommitmentDefinition(),
6854
7306
  new OpenCommitmentDefinition(),
6855
7307
  new ClosedCommitmentDefinition(),
7308
+ new TeamCommitmentDefinition(),
6856
7309
  new UseBrowserCommitmentDefinition(),
6857
7310
  new UseSearchEngineCommitmentDefinition(),
6858
7311
  new UseTimeCommitmentDefinition(),