@promptbook/core 0.100.0-2 → 0.100.0-21
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/README.md +1 -0
- package/esm/index.es.js +1685 -203
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/core.index.d.ts +24 -0
- package/esm/typings/src/_packages/types.index.d.ts +28 -0
- package/esm/typings/src/book-2.0/agent-source/parseAgentSource.d.ts +30 -0
- package/esm/typings/src/book-2.0/agent-source/parseAgentSource.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/agent-source/string_book.d.ts +26 -0
- package/esm/typings/src/book-2.0/commitments/ACTION/ACTION.d.ts +30 -0
- package/esm/typings/src/book-2.0/commitments/FORMAT/FORMAT.d.ts +31 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/FrontendRAGService.d.ts +48 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/KNOWLEDGE.d.ts +43 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/RAGService.d.ts +54 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/processors/BaseKnowledgeProcessor.d.ts +45 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/processors/PdfProcessor.d.ts +31 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/processors/ProcessorFactory.d.ts +23 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/processors/TextProcessor.d.ts +18 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/types.d.ts +56 -0
- package/esm/typings/src/book-2.0/commitments/KNOWLEDGE/utils/ragHelper.d.ts +34 -0
- package/esm/typings/src/book-2.0/commitments/META_IMAGE/META_IMAGE.d.ts +36 -0
- package/esm/typings/src/book-2.0/commitments/META_LINK/META_LINK.d.ts +48 -0
- package/esm/typings/src/book-2.0/commitments/MODEL/MODEL.d.ts +31 -0
- package/esm/typings/src/book-2.0/commitments/NOTE/NOTE.d.ts +41 -0
- package/esm/typings/src/book-2.0/commitments/PERSONA/PERSONA.d.ts +38 -0
- package/esm/typings/src/book-2.0/commitments/RULE/RULE.d.ts +36 -0
- package/esm/typings/src/book-2.0/commitments/SAMPLE/SAMPLE.d.ts +36 -0
- package/esm/typings/src/book-2.0/commitments/STYLE/STYLE.d.ts +30 -0
- package/esm/typings/src/book-2.0/commitments/_base/BaseCommitmentDefinition.d.ts +42 -0
- package/esm/typings/src/book-2.0/commitments/_base/BookCommitment.d.ts +5 -0
- package/esm/typings/src/book-2.0/commitments/_base/CommitmentDefinition.d.ts +37 -0
- package/esm/typings/src/book-2.0/commitments/_base/NotYetImplementedCommitmentDefinition.d.ts +14 -0
- package/esm/typings/src/book-2.0/commitments/_base/createEmptyAgentModelRequirements.d.ts +19 -0
- package/esm/typings/src/book-2.0/commitments/_misc/AgentModelRequirements.d.ts +37 -0
- package/esm/typings/src/book-2.0/commitments/_misc/AgentSourceParseResult.d.ts +18 -0
- package/esm/typings/src/book-2.0/commitments/_misc/ParsedCommitment.d.ts +22 -0
- package/esm/typings/src/book-2.0/commitments/_misc/createAgentModelRequirements.d.ts +61 -0
- package/esm/typings/src/book-2.0/commitments/_misc/createAgentModelRequirementsWithCommitments.d.ts +35 -0
- package/esm/typings/src/book-2.0/commitments/_misc/createCommitmentRegex.d.ts +20 -0
- package/esm/typings/src/book-2.0/commitments/_misc/parseAgentSourceWithCommitments.d.ts +24 -0
- package/esm/typings/src/book-2.0/commitments/_misc/removeCommentsFromSystemMessage.d.ts +11 -0
- package/esm/typings/src/book-2.0/commitments/index.d.ts +56 -0
- package/esm/typings/src/book-2.0/utils/profileImageUtils.d.ts +39 -0
- package/esm/typings/src/pipeline/book-notation.d.ts +2 -1
- package/esm/typings/src/types/typeAliases.d.ts +6 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/esm/typings/src/wizard/wizard.d.ts +14 -4
- package/package.json +1 -1
- package/umd/index.umd.js +1696 -202
- package/umd/index.umd.js.map +1 -1
package/umd/index.umd.js
CHANGED
|
@@ -27,136 +27,258 @@
|
|
|
27
27
|
* @generated
|
|
28
28
|
* @see https://github.com/webgptorg/promptbook
|
|
29
29
|
*/
|
|
30
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.100.0-
|
|
30
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.100.0-21';
|
|
31
31
|
/**
|
|
32
32
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
33
33
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* Extracts profile image URL from agent definition text and returns cleaned system message
|
|
38
|
+
* @param systemMessage The original system message that may contain META IMAGE line
|
|
39
|
+
* @returns Object with profileImageUrl (if found) and cleanedSystemMessage (without META IMAGE line)
|
|
38
40
|
*
|
|
39
|
-
*
|
|
41
|
+
* @private - TODO: [🧠] Maybe should be public?
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* Generates a gravatar URL based on agent name for fallback avatar
|
|
45
|
+
* @param name The agent name to generate avatar for
|
|
46
|
+
* @returns Gravatar URL
|
|
40
47
|
*
|
|
41
|
-
* @
|
|
48
|
+
* @private - TODO: [🧠] Maybe should be public?
|
|
42
49
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
50
|
+
function generateGravatarUrl(name) {
|
|
51
|
+
// Use a default name if none provided
|
|
52
|
+
const safeName = name || 'Anonymous Agent';
|
|
53
|
+
// Create a simple hash from the name for consistent avatar
|
|
54
|
+
let hash = 0;
|
|
55
|
+
for (let i = 0; i < safeName.length; i++) {
|
|
56
|
+
const char = safeName.charCodeAt(i);
|
|
57
|
+
hash = (hash << 5) - hash + char;
|
|
58
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
59
|
+
}
|
|
60
|
+
const avatarId = Math.abs(hash).toString();
|
|
61
|
+
return `https://www.gravatar.com/avatar/${avatarId}?default=robohash&size=200&rating=x`;
|
|
47
62
|
}
|
|
48
63
|
/**
|
|
49
|
-
*
|
|
64
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
50
65
|
*/
|
|
51
66
|
|
|
52
67
|
/**
|
|
53
|
-
*
|
|
68
|
+
* Freezes the given object and all its nested objects recursively
|
|
69
|
+
*
|
|
70
|
+
* Note: `$` is used to indicate that this function is not a pure function - it mutates given object
|
|
71
|
+
* Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
|
|
54
72
|
*
|
|
73
|
+
* @returns The same object as the input, but deeply frozen
|
|
55
74
|
* @public exported from `@promptbook/utils`
|
|
56
75
|
*/
|
|
57
|
-
function
|
|
58
|
-
if (
|
|
59
|
-
return
|
|
76
|
+
function $deepFreeze(objectValue) {
|
|
77
|
+
if (Array.isArray(objectValue)) {
|
|
78
|
+
return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
|
|
60
79
|
}
|
|
61
|
-
|
|
62
|
-
|
|
80
|
+
const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
81
|
+
for (const propertyName of propertyNames) {
|
|
82
|
+
const value = objectValue[propertyName];
|
|
83
|
+
if (value && typeof value === 'object') {
|
|
84
|
+
$deepFreeze(value);
|
|
85
|
+
}
|
|
63
86
|
}
|
|
64
|
-
|
|
87
|
+
Object.freeze(objectValue);
|
|
88
|
+
return objectValue;
|
|
65
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
92
|
+
*/
|
|
66
93
|
|
|
67
94
|
/**
|
|
68
|
-
*
|
|
95
|
+
* Generates a regex pattern to match a specific commitment
|
|
69
96
|
*
|
|
70
|
-
* Note:
|
|
71
|
-
*
|
|
97
|
+
* Note: It always creates new Regex object
|
|
98
|
+
* Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
|
|
99
|
+
*
|
|
100
|
+
* @private
|
|
72
101
|
*/
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
102
|
+
function createCommitmentRegex(commitment) {
|
|
103
|
+
const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
104
|
+
const keywordPattern = escapedCommitment.split(/\s+/).join('\\s+');
|
|
105
|
+
const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b\\s+(?<contents>.+)$`, 'gim');
|
|
106
|
+
return regex;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Generates a regex pattern to match a specific commitment type
|
|
110
|
+
*
|
|
111
|
+
* Note: It just matches the type part of the commitment
|
|
112
|
+
* Note: It always creates new Regex object
|
|
113
|
+
* Note: Uses word boundaries to ensure only full words are matched (e.g., "PERSONA" matches but "PERSONALITY" does not)
|
|
114
|
+
*
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
function createCommitmentTypeRegex(commitment) {
|
|
118
|
+
const escapedCommitment = commitment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
119
|
+
const keywordPattern = escapedCommitment.split(/\s+/).join('\\s+');
|
|
120
|
+
const regex = new RegExp(`^\\s*(?<type>${keywordPattern})\\b`, 'gim');
|
|
121
|
+
return regex;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Base implementation of CommitmentDefinition that provides common functionality
|
|
126
|
+
* Most commitments can extend this class and only override the applyToAgentModelRequirements method
|
|
127
|
+
*
|
|
128
|
+
* @private
|
|
129
|
+
*/
|
|
130
|
+
class BaseCommitmentDefinition {
|
|
131
|
+
constructor(type) {
|
|
132
|
+
this.type = type;
|
|
76
133
|
}
|
|
77
|
-
|
|
78
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Creates a regex pattern to match this commitment in agent source
|
|
136
|
+
* Uses the existing createCommitmentRegex function as internal helper
|
|
137
|
+
*/
|
|
138
|
+
createRegex() {
|
|
139
|
+
return createCommitmentRegex(this.type);
|
|
79
140
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Creates a regex pattern to match just the commitment type
|
|
143
|
+
* Uses the existing createCommitmentTypeRegex function as internal helper
|
|
144
|
+
*/
|
|
145
|
+
createTypeRegex() {
|
|
146
|
+
return createCommitmentTypeRegex(this.type);
|
|
83
147
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Helper method to create a new requirements object with updated system message
|
|
150
|
+
* This is commonly used by many commitments
|
|
151
|
+
*/
|
|
152
|
+
updateSystemMessage(requirements, messageUpdate) {
|
|
153
|
+
const newMessage = typeof messageUpdate === 'string' ? messageUpdate : messageUpdate(requirements.systemMessage);
|
|
154
|
+
return {
|
|
155
|
+
...requirements,
|
|
156
|
+
systemMessage: newMessage,
|
|
157
|
+
};
|
|
89
158
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Helper method to append content to the system message
|
|
161
|
+
*/
|
|
162
|
+
appendToSystemMessage(requirements, content, separator = '\n\n') {
|
|
163
|
+
return this.updateSystemMessage(requirements, (currentMessage) => {
|
|
164
|
+
if (!currentMessage.trim()) {
|
|
165
|
+
return content;
|
|
166
|
+
}
|
|
167
|
+
return currentMessage + separator + content;
|
|
168
|
+
});
|
|
94
169
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Helper method to add a comment section to the system message
|
|
172
|
+
* Comments are lines starting with # that will be removed from the final system message
|
|
173
|
+
* but can be useful for organizing and structuring the message during processing
|
|
174
|
+
*/
|
|
175
|
+
addCommentSection(requirements, commentTitle, content, position = 'end') {
|
|
176
|
+
const commentSection = `# ${commentTitle.toUpperCase()}\n${content}`;
|
|
177
|
+
if (position === 'beginning') {
|
|
178
|
+
return this.updateSystemMessage(requirements, (currentMessage) => {
|
|
179
|
+
if (!currentMessage.trim()) {
|
|
180
|
+
return commentSection;
|
|
181
|
+
}
|
|
182
|
+
return commentSection + '\n\n' + currentMessage;
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
return this.appendToSystemMessage(requirements, commentSection);
|
|
187
|
+
}
|
|
99
188
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* ACTION commitment definition
|
|
193
|
+
*
|
|
194
|
+
* The ACTION commitment defines specific actions or capabilities that the agent can perform.
|
|
195
|
+
* This helps define what the agent is capable of doing and how it should approach tasks.
|
|
196
|
+
*
|
|
197
|
+
* Example usage in agent source:
|
|
198
|
+
*
|
|
199
|
+
* ```book
|
|
200
|
+
* ACTION Can generate code snippets and explain programming concepts
|
|
201
|
+
* ACTION Able to analyze data and provide insights
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
205
|
+
*/
|
|
206
|
+
class ActionCommitmentDefinition extends BaseCommitmentDefinition {
|
|
207
|
+
constructor() {
|
|
208
|
+
super('ACTION');
|
|
104
209
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
210
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
211
|
+
const trimmedContent = content.trim();
|
|
212
|
+
if (!trimmedContent) {
|
|
213
|
+
return requirements;
|
|
214
|
+
}
|
|
215
|
+
// Add action capability to the system message
|
|
216
|
+
const actionSection = `Capability: ${trimmedContent}`;
|
|
217
|
+
return this.appendToSystemMessage(requirements, actionSection, '\n\n');
|
|
109
218
|
}
|
|
110
|
-
return false;
|
|
111
219
|
}
|
|
112
220
|
/**
|
|
113
|
-
*
|
|
221
|
+
* Singleton instance of the ACTION commitment definition
|
|
222
|
+
*
|
|
223
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
224
|
+
*/
|
|
225
|
+
new ActionCommitmentDefinition();
|
|
226
|
+
/**
|
|
227
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
114
228
|
*/
|
|
115
229
|
|
|
116
230
|
/**
|
|
117
|
-
*
|
|
231
|
+
* FORMAT commitment definition
|
|
118
232
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
233
|
+
* The FORMAT commitment defines the specific output structure and formatting
|
|
234
|
+
* that the agent should use in its responses. This includes data formats,
|
|
235
|
+
* response templates, and structural requirements.
|
|
123
236
|
*
|
|
124
|
-
*
|
|
237
|
+
* Example usage in agent source:
|
|
238
|
+
*
|
|
239
|
+
* ```book
|
|
240
|
+
* FORMAT Always respond in JSON format with 'status' and 'data' fields
|
|
241
|
+
* FORMAT Use markdown formatting for all code blocks
|
|
242
|
+
* ```
|
|
243
|
+
*
|
|
244
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
125
245
|
*/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
246
|
+
class FormatCommitmentDefinition extends BaseCommitmentDefinition {
|
|
247
|
+
constructor() {
|
|
248
|
+
super('FORMAT');
|
|
129
249
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const urlObject = new URL(url /* because fail is handled */);
|
|
135
|
-
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
136
|
-
return false;
|
|
250
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
251
|
+
const trimmedContent = content.trim();
|
|
252
|
+
if (!trimmedContent) {
|
|
253
|
+
return requirements;
|
|
137
254
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return false;
|
|
255
|
+
// Add format instructions to the system message
|
|
256
|
+
const formatSection = `Output Format: ${trimmedContent}`;
|
|
257
|
+
return this.appendToSystemMessage(requirements, formatSection, '\n\n');
|
|
142
258
|
}
|
|
143
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Singleton instance of the FORMAT commitment definition
|
|
262
|
+
*
|
|
263
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
264
|
+
*/
|
|
265
|
+
new FormatCommitmentDefinition();
|
|
266
|
+
/**
|
|
267
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
268
|
+
*/
|
|
144
269
|
|
|
145
270
|
/**
|
|
146
|
-
*
|
|
271
|
+
* Error thrown when a fetch request fails
|
|
147
272
|
*
|
|
148
273
|
* @public exported from `@promptbook/core`
|
|
149
274
|
*/
|
|
150
|
-
class
|
|
275
|
+
class PromptbookFetchError extends Error {
|
|
151
276
|
constructor(message) {
|
|
152
277
|
super(message);
|
|
153
|
-
this.name = '
|
|
154
|
-
Object.setPrototypeOf(this,
|
|
278
|
+
this.name = 'PromptbookFetchError';
|
|
279
|
+
Object.setPrototypeOf(this, PromptbookFetchError.prototype);
|
|
155
280
|
}
|
|
156
281
|
}
|
|
157
|
-
/**
|
|
158
|
-
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
159
|
-
*/
|
|
160
282
|
|
|
161
283
|
/**
|
|
162
284
|
* Available remote servers for the Promptbook
|
|
@@ -553,76 +675,1480 @@
|
|
|
553
675
|
}
|
|
554
676
|
|
|
555
677
|
/**
|
|
556
|
-
* This error type indicates that the error should not happen and its last check before crashing with some other error
|
|
678
|
+
* This error type indicates that the error should not happen and its last check before crashing with some other error
|
|
679
|
+
*
|
|
680
|
+
* @public exported from `@promptbook/core`
|
|
681
|
+
*/
|
|
682
|
+
class UnexpectedError extends Error {
|
|
683
|
+
constructor(message) {
|
|
684
|
+
super(spaceTrim.spaceTrim((block) => `
|
|
685
|
+
${block(message)}
|
|
686
|
+
|
|
687
|
+
Note: This error should not happen.
|
|
688
|
+
It's probably a bug in the pipeline collection
|
|
689
|
+
|
|
690
|
+
Please report issue:
|
|
691
|
+
${block(getErrorReportUrl(new Error(message)).href)}
|
|
692
|
+
|
|
693
|
+
Or contact us on ${ADMIN_EMAIL}
|
|
694
|
+
|
|
695
|
+
`));
|
|
696
|
+
this.name = 'UnexpectedError';
|
|
697
|
+
Object.setPrototypeOf(this, UnexpectedError.prototype);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
|
|
703
|
+
*
|
|
704
|
+
* @public exported from `@promptbook/core`
|
|
705
|
+
*/
|
|
706
|
+
class WrappedError extends Error {
|
|
707
|
+
constructor(whatWasThrown) {
|
|
708
|
+
const tag = `[🤮]`;
|
|
709
|
+
console.error(tag, whatWasThrown);
|
|
710
|
+
super(spaceTrim.spaceTrim(`
|
|
711
|
+
Non-Error object was thrown
|
|
712
|
+
|
|
713
|
+
Note: Look for ${tag} in the console for more details
|
|
714
|
+
Please report issue on ${ADMIN_EMAIL}
|
|
715
|
+
`));
|
|
716
|
+
this.name = 'WrappedError';
|
|
717
|
+
Object.setPrototypeOf(this, WrappedError.prototype);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Helper used in catch blocks to assert that the error is an instance of `Error`
|
|
723
|
+
*
|
|
724
|
+
* @param whatWasThrown Any object that was thrown
|
|
725
|
+
* @returns Nothing if the error is an instance of `Error`
|
|
726
|
+
* @throws `WrappedError` or `UnexpectedError` if the error is not standard
|
|
727
|
+
*
|
|
728
|
+
* @private within the repository
|
|
729
|
+
*/
|
|
730
|
+
function assertsError(whatWasThrown) {
|
|
731
|
+
// Case 1: Handle error which was rethrown as `WrappedError`
|
|
732
|
+
if (whatWasThrown instanceof WrappedError) {
|
|
733
|
+
const wrappedError = whatWasThrown;
|
|
734
|
+
throw wrappedError;
|
|
735
|
+
}
|
|
736
|
+
// Case 2: Handle unexpected errors
|
|
737
|
+
if (whatWasThrown instanceof UnexpectedError) {
|
|
738
|
+
const unexpectedError = whatWasThrown;
|
|
739
|
+
throw unexpectedError;
|
|
740
|
+
}
|
|
741
|
+
// Case 3: Handle standard errors - keep them up to consumer
|
|
742
|
+
if (whatWasThrown instanceof Error) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
// Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
|
|
746
|
+
throw new WrappedError(whatWasThrown);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
|
|
751
|
+
*
|
|
752
|
+
* @public exported from `@promptbook/core`
|
|
753
|
+
*/
|
|
754
|
+
const promptbookFetch = async (urlOrRequest, init) => {
|
|
755
|
+
try {
|
|
756
|
+
return await fetch(urlOrRequest, init);
|
|
757
|
+
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
assertsError(error);
|
|
760
|
+
let url;
|
|
761
|
+
if (typeof urlOrRequest === 'string') {
|
|
762
|
+
url = urlOrRequest;
|
|
763
|
+
}
|
|
764
|
+
else if (urlOrRequest instanceof Request) {
|
|
765
|
+
url = urlOrRequest.url;
|
|
766
|
+
}
|
|
767
|
+
throw new PromptbookFetchError(spaceTrim__default["default"]((block) => `
|
|
768
|
+
Can not fetch "${url}"
|
|
769
|
+
|
|
770
|
+
Fetch error:
|
|
771
|
+
${block(error.message)}
|
|
772
|
+
|
|
773
|
+
`));
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
/**
|
|
777
|
+
* TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
|
|
778
|
+
*/
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Frontend RAG Service that uses backend APIs for processing
|
|
782
|
+
* This avoids Node.js dependencies in the frontend
|
|
783
|
+
*
|
|
784
|
+
* @private - TODO: [🧠] Maybe should be public?
|
|
785
|
+
*/
|
|
786
|
+
class FrontendRAGService {
|
|
787
|
+
constructor(config) {
|
|
788
|
+
this.chunks = [];
|
|
789
|
+
this.sources = [];
|
|
790
|
+
this.isInitialized = false;
|
|
791
|
+
this.config = {
|
|
792
|
+
maxChunkSize: 1000,
|
|
793
|
+
chunkOverlap: 200,
|
|
794
|
+
maxRetrievedChunks: 5,
|
|
795
|
+
minRelevanceScore: 0.1,
|
|
796
|
+
...config,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Initialize knowledge sources by processing them on the backend
|
|
801
|
+
*/
|
|
802
|
+
async initializeKnowledgeSources(sources) {
|
|
803
|
+
if (sources.length === 0) {
|
|
804
|
+
this.isInitialized = true;
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
const response = await promptbookFetch('/api/knowledge/process-sources', {
|
|
809
|
+
method: 'POST',
|
|
810
|
+
headers: {
|
|
811
|
+
'Content-Type': 'application/json',
|
|
812
|
+
},
|
|
813
|
+
body: JSON.stringify({
|
|
814
|
+
sources,
|
|
815
|
+
config: {
|
|
816
|
+
maxChunkSize: this.config.maxChunkSize,
|
|
817
|
+
chunkOverlap: this.config.chunkOverlap,
|
|
818
|
+
},
|
|
819
|
+
}),
|
|
820
|
+
});
|
|
821
|
+
if (!response.ok) {
|
|
822
|
+
throw new Error(`Failed to process knowledge sources: ${response.status}`);
|
|
823
|
+
}
|
|
824
|
+
const result = (await response.json());
|
|
825
|
+
if (!result.success) {
|
|
826
|
+
throw new Error(result.message || 'Failed to process knowledge sources');
|
|
827
|
+
}
|
|
828
|
+
this.chunks = result.chunks;
|
|
829
|
+
this.sources = sources;
|
|
830
|
+
this.isInitialized = true;
|
|
831
|
+
console.log(`Initialized RAG service with ${this.chunks.length} chunks from ${sources.length} sources`);
|
|
832
|
+
}
|
|
833
|
+
catch (error) {
|
|
834
|
+
console.error('Failed to initialize knowledge sources:', error);
|
|
835
|
+
// Don't throw - allow the system to continue without RAG
|
|
836
|
+
this.isInitialized = true;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Get relevant context for a user query
|
|
841
|
+
*/
|
|
842
|
+
async getContextForQuery(query) {
|
|
843
|
+
if (!this.isInitialized) {
|
|
844
|
+
console.warn('RAG service not initialized');
|
|
845
|
+
return '';
|
|
846
|
+
}
|
|
847
|
+
if (this.chunks.length === 0) {
|
|
848
|
+
return '';
|
|
849
|
+
}
|
|
850
|
+
try {
|
|
851
|
+
const response = await promptbookFetch('/api/knowledge/retrieve-context', {
|
|
852
|
+
method: 'POST',
|
|
853
|
+
headers: {
|
|
854
|
+
'Content-Type': 'application/json',
|
|
855
|
+
},
|
|
856
|
+
body: JSON.stringify({
|
|
857
|
+
query,
|
|
858
|
+
chunks: this.chunks,
|
|
859
|
+
config: {
|
|
860
|
+
maxRetrievedChunks: this.config.maxRetrievedChunks,
|
|
861
|
+
minRelevanceScore: this.config.minRelevanceScore,
|
|
862
|
+
},
|
|
863
|
+
}),
|
|
864
|
+
});
|
|
865
|
+
if (!response.ok) {
|
|
866
|
+
console.error(`Failed to retrieve context: ${response.status}`);
|
|
867
|
+
return '';
|
|
868
|
+
}
|
|
869
|
+
const result = (await response.json());
|
|
870
|
+
if (!result.success) {
|
|
871
|
+
console.error('Context retrieval failed:', result.message);
|
|
872
|
+
return '';
|
|
873
|
+
}
|
|
874
|
+
return result.context;
|
|
875
|
+
}
|
|
876
|
+
catch (error) {
|
|
877
|
+
console.error('Error retrieving context:', error);
|
|
878
|
+
return '';
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Get relevant chunks for a query (for debugging/inspection)
|
|
883
|
+
*/
|
|
884
|
+
async getRelevantChunks(query) {
|
|
885
|
+
if (!this.isInitialized || this.chunks.length === 0) {
|
|
886
|
+
return [];
|
|
887
|
+
}
|
|
888
|
+
try {
|
|
889
|
+
const response = await promptbookFetch('/api/knowledge/retrieve-context', {
|
|
890
|
+
method: 'POST',
|
|
891
|
+
headers: {
|
|
892
|
+
'Content-Type': 'application/json',
|
|
893
|
+
},
|
|
894
|
+
body: JSON.stringify({
|
|
895
|
+
query,
|
|
896
|
+
chunks: this.chunks,
|
|
897
|
+
config: {
|
|
898
|
+
maxRetrievedChunks: this.config.maxRetrievedChunks,
|
|
899
|
+
minRelevanceScore: this.config.minRelevanceScore,
|
|
900
|
+
},
|
|
901
|
+
}),
|
|
902
|
+
});
|
|
903
|
+
if (!response.ok) {
|
|
904
|
+
return [];
|
|
905
|
+
}
|
|
906
|
+
const result = (await response.json());
|
|
907
|
+
return result.success ? result.relevantChunks : [];
|
|
908
|
+
}
|
|
909
|
+
catch (error) {
|
|
910
|
+
console.error('Error retrieving relevant chunks:', error);
|
|
911
|
+
return [];
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Get knowledge base statistics
|
|
916
|
+
*/
|
|
917
|
+
getStats() {
|
|
918
|
+
return {
|
|
919
|
+
sources: this.sources.length,
|
|
920
|
+
chunks: this.chunks.length,
|
|
921
|
+
isInitialized: this.isInitialized,
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Check if the service is ready to use
|
|
926
|
+
*/
|
|
927
|
+
isReady() {
|
|
928
|
+
return this.isInitialized;
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Clear all knowledge sources
|
|
932
|
+
*/
|
|
933
|
+
clearKnowledgeBase() {
|
|
934
|
+
this.chunks = [];
|
|
935
|
+
this.sources = [];
|
|
936
|
+
this.isInitialized = false;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Add a single knowledge source (for incremental updates)
|
|
940
|
+
*/
|
|
941
|
+
async addKnowledgeSource(url) {
|
|
942
|
+
if (this.sources.includes(url)) {
|
|
943
|
+
console.log(`Knowledge source already exists: ${url}`);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
const response = await promptbookFetch('/api/knowledge/process-sources', {
|
|
948
|
+
method: 'POST',
|
|
949
|
+
headers: {
|
|
950
|
+
'Content-Type': 'application/json',
|
|
951
|
+
},
|
|
952
|
+
body: JSON.stringify({
|
|
953
|
+
sources: [url],
|
|
954
|
+
config: {
|
|
955
|
+
maxChunkSize: this.config.maxChunkSize,
|
|
956
|
+
chunkOverlap: this.config.chunkOverlap,
|
|
957
|
+
},
|
|
958
|
+
}),
|
|
959
|
+
});
|
|
960
|
+
if (!response.ok) {
|
|
961
|
+
throw new Error(`Failed to process knowledge source: ${response.status}`);
|
|
962
|
+
}
|
|
963
|
+
const result = (await response.json());
|
|
964
|
+
if (!result.success) {
|
|
965
|
+
throw new Error(result.message || 'Failed to process knowledge source');
|
|
966
|
+
}
|
|
967
|
+
// Add new chunks to existing ones
|
|
968
|
+
this.chunks.push(...result.chunks);
|
|
969
|
+
this.sources.push(url);
|
|
970
|
+
console.log(`Added knowledge source: ${url} (${result.chunks.length} chunks)`);
|
|
971
|
+
}
|
|
972
|
+
catch (error) {
|
|
973
|
+
console.error(`Failed to add knowledge source ${url}:`, error);
|
|
974
|
+
throw error;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* KNOWLEDGE commitment definition
|
|
981
|
+
*
|
|
982
|
+
* The KNOWLEDGE commitment adds specific knowledge, facts, or context to the agent
|
|
983
|
+
* using RAG (Retrieval-Augmented Generation) approach for external sources.
|
|
984
|
+
*
|
|
985
|
+
* Supports both direct text knowledge and external sources like PDFs.
|
|
986
|
+
*
|
|
987
|
+
* Example usage in agent source:
|
|
988
|
+
*
|
|
989
|
+
* ```book
|
|
990
|
+
* KNOWLEDGE The company was founded in 2020 and specializes in AI-powered solutions
|
|
991
|
+
* KNOWLEDGE https://example.com/company-handbook.pdf
|
|
992
|
+
* KNOWLEDGE https://example.com/product-documentation.pdf
|
|
993
|
+
* ```
|
|
994
|
+
*
|
|
995
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
996
|
+
*/
|
|
997
|
+
class KnowledgeCommitmentDefinition extends BaseCommitmentDefinition {
|
|
998
|
+
constructor() {
|
|
999
|
+
super('KNOWLEDGE');
|
|
1000
|
+
this.ragService = new FrontendRAGService();
|
|
1001
|
+
}
|
|
1002
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1003
|
+
var _a;
|
|
1004
|
+
const trimmedContent = content.trim();
|
|
1005
|
+
if (!trimmedContent) {
|
|
1006
|
+
return requirements;
|
|
1007
|
+
}
|
|
1008
|
+
// Check if content is a URL (external knowledge source)
|
|
1009
|
+
if (this.isUrl(trimmedContent)) {
|
|
1010
|
+
// Store the URL for later async processing
|
|
1011
|
+
const updatedRequirements = {
|
|
1012
|
+
...requirements,
|
|
1013
|
+
metadata: {
|
|
1014
|
+
...requirements.metadata,
|
|
1015
|
+
ragService: this.ragService,
|
|
1016
|
+
knowledgeSources: [
|
|
1017
|
+
...(((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.knowledgeSources) || []),
|
|
1018
|
+
trimmedContent,
|
|
1019
|
+
],
|
|
1020
|
+
},
|
|
1021
|
+
};
|
|
1022
|
+
// Add placeholder information about knowledge sources to system message
|
|
1023
|
+
const knowledgeInfo = `Knowledge Source URL: ${trimmedContent} (will be processed for retrieval during chat)`;
|
|
1024
|
+
return this.appendToSystemMessage(updatedRequirements, knowledgeInfo, '\n\n');
|
|
1025
|
+
}
|
|
1026
|
+
else {
|
|
1027
|
+
// Direct text knowledge - add to system message
|
|
1028
|
+
const knowledgeSection = `Knowledge: ${trimmedContent}`;
|
|
1029
|
+
return this.appendToSystemMessage(requirements, knowledgeSection, '\n\n');
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Check if content is a URL
|
|
1034
|
+
*/
|
|
1035
|
+
isUrl(content) {
|
|
1036
|
+
try {
|
|
1037
|
+
new URL(content);
|
|
1038
|
+
return true;
|
|
1039
|
+
}
|
|
1040
|
+
catch (_a) {
|
|
1041
|
+
return false;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Get RAG service instance for retrieving context during chat
|
|
1046
|
+
*/
|
|
1047
|
+
getRagService() {
|
|
1048
|
+
return this.ragService;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Singleton instance of the KNOWLEDGE commitment definition
|
|
1053
|
+
*
|
|
1054
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1055
|
+
*/
|
|
1056
|
+
new KnowledgeCommitmentDefinition();
|
|
1057
|
+
/**
|
|
1058
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1059
|
+
*/
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* META IMAGE commitment definition
|
|
1063
|
+
*
|
|
1064
|
+
* The META IMAGE commitment sets the agent's avatar/profile image URL.
|
|
1065
|
+
* This commitment is special because it doesn't affect the system message,
|
|
1066
|
+
* but is handled separately in the parsing logic.
|
|
1067
|
+
*
|
|
1068
|
+
* Example usage in agent source:
|
|
1069
|
+
*
|
|
1070
|
+
* ```book
|
|
1071
|
+
* META IMAGE https://example.com/avatar.jpg
|
|
1072
|
+
* META IMAGE /assets/agent-avatar.png
|
|
1073
|
+
* ```
|
|
1074
|
+
*
|
|
1075
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1076
|
+
*/
|
|
1077
|
+
class MetaImageCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1078
|
+
constructor() {
|
|
1079
|
+
super('META IMAGE');
|
|
1080
|
+
}
|
|
1081
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1082
|
+
// META IMAGE doesn't modify the system message or model requirements
|
|
1083
|
+
// It's handled separately in the parsing logic for profile image extraction
|
|
1084
|
+
// This method exists for consistency with the CommitmentDefinition interface
|
|
1085
|
+
return requirements;
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Extracts the profile image URL from the content
|
|
1089
|
+
* This is used by the parsing logic
|
|
1090
|
+
*/
|
|
1091
|
+
extractProfileImageUrl(content) {
|
|
1092
|
+
const trimmedContent = content.trim();
|
|
1093
|
+
return trimmedContent || null;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Singleton instance of the META IMAGE commitment definition
|
|
1098
|
+
*
|
|
1099
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1100
|
+
*/
|
|
1101
|
+
new MetaImageCommitmentDefinition();
|
|
1102
|
+
/**
|
|
1103
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1104
|
+
*/
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* META LINK commitment definition
|
|
1108
|
+
*
|
|
1109
|
+
* The `META LINK` commitment represents the link to the person from whom the agent is created.
|
|
1110
|
+
* This commitment is special because it doesn't affect the system message,
|
|
1111
|
+
* but is handled separately in the parsing logic for profile display.
|
|
1112
|
+
*
|
|
1113
|
+
* Example usage in agent source:
|
|
1114
|
+
*
|
|
1115
|
+
* ```
|
|
1116
|
+
* META LINK https://twitter.com/username
|
|
1117
|
+
* META LINK https://linkedin.com/in/profile
|
|
1118
|
+
* META LINK https://github.com/username
|
|
1119
|
+
* ```
|
|
1120
|
+
*
|
|
1121
|
+
* Multiple `META LINK` commitments can be used when there are multiple sources:
|
|
1122
|
+
*
|
|
1123
|
+
* ```book
|
|
1124
|
+
* META LINK https://twitter.com/username
|
|
1125
|
+
* META LINK https://linkedin.com/in/profile
|
|
1126
|
+
* ```
|
|
1127
|
+
*
|
|
1128
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1129
|
+
*/
|
|
1130
|
+
class MetaLinkCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1131
|
+
constructor() {
|
|
1132
|
+
super('META LINK');
|
|
1133
|
+
}
|
|
1134
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1135
|
+
// META LINK doesn't modify the system message or model requirements
|
|
1136
|
+
// It's handled separately in the parsing logic for profile link extraction
|
|
1137
|
+
// This method exists for consistency with the CommitmentDefinition interface
|
|
1138
|
+
return requirements;
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Extracts the profile link URL from the content
|
|
1142
|
+
* This is used by the parsing logic
|
|
1143
|
+
*/
|
|
1144
|
+
extractProfileLinkUrl(content) {
|
|
1145
|
+
const trimmedContent = content.trim();
|
|
1146
|
+
return trimmedContent || null;
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Validates if the provided content is a valid URL
|
|
1150
|
+
*/
|
|
1151
|
+
isValidUrl(content) {
|
|
1152
|
+
try {
|
|
1153
|
+
new URL(content.trim());
|
|
1154
|
+
return true;
|
|
1155
|
+
}
|
|
1156
|
+
catch (_a) {
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Singleton instance of the META LINK commitment definition
|
|
1163
|
+
*
|
|
1164
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1165
|
+
*/
|
|
1166
|
+
new MetaLinkCommitmentDefinition();
|
|
1167
|
+
/**
|
|
1168
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1169
|
+
*/
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* MODEL commitment definition
|
|
1173
|
+
*
|
|
1174
|
+
* The MODEL commitment specifies which AI model to use and can also set
|
|
1175
|
+
* model-specific parameters like temperature, topP, and topK.
|
|
1176
|
+
*
|
|
1177
|
+
* Example usage in agent source:
|
|
1178
|
+
*
|
|
1179
|
+
* ```book
|
|
1180
|
+
* MODEL gpt-4
|
|
1181
|
+
* MODEL claude-3-opus temperature=0.3
|
|
1182
|
+
* MODEL gpt-3.5-turbo temperature=0.8 topP=0.9
|
|
1183
|
+
* ```
|
|
1184
|
+
*
|
|
1185
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1186
|
+
*/
|
|
1187
|
+
class ModelCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1188
|
+
constructor() {
|
|
1189
|
+
super('MODEL');
|
|
1190
|
+
}
|
|
1191
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1192
|
+
const trimmedContent = content.trim();
|
|
1193
|
+
if (!trimmedContent) {
|
|
1194
|
+
return requirements;
|
|
1195
|
+
}
|
|
1196
|
+
// Parse the model specification
|
|
1197
|
+
const parts = trimmedContent.split(/\s+/);
|
|
1198
|
+
const modelName = parts[0];
|
|
1199
|
+
if (!modelName) {
|
|
1200
|
+
return requirements;
|
|
1201
|
+
}
|
|
1202
|
+
// Start with the model name
|
|
1203
|
+
const updatedRequirements = {
|
|
1204
|
+
...requirements,
|
|
1205
|
+
modelName,
|
|
1206
|
+
};
|
|
1207
|
+
// Parse additional parameters
|
|
1208
|
+
const result = { ...updatedRequirements };
|
|
1209
|
+
for (let i = 1; i < parts.length; i++) {
|
|
1210
|
+
const param = parts[i];
|
|
1211
|
+
if (param && param.includes('=')) {
|
|
1212
|
+
const [key, value] = param.split('=');
|
|
1213
|
+
if (key && value) {
|
|
1214
|
+
const numValue = parseFloat(value);
|
|
1215
|
+
if (!isNaN(numValue)) {
|
|
1216
|
+
switch (key.toLowerCase()) {
|
|
1217
|
+
case 'temperature':
|
|
1218
|
+
result.temperature = numValue;
|
|
1219
|
+
break;
|
|
1220
|
+
case 'topp':
|
|
1221
|
+
case 'top_p':
|
|
1222
|
+
result.topP = numValue;
|
|
1223
|
+
break;
|
|
1224
|
+
case 'topk':
|
|
1225
|
+
case 'top_k':
|
|
1226
|
+
result.topK = Math.round(numValue);
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return result;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Singleton instance of the MODEL commitment definition
|
|
1238
|
+
*
|
|
1239
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1240
|
+
*/
|
|
1241
|
+
new ModelCommitmentDefinition();
|
|
1242
|
+
/**
|
|
1243
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1244
|
+
*/
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* NOTE commitment definition
|
|
1248
|
+
*
|
|
1249
|
+
* The NOTE commitment is used to add comments to the agent source without making any changes
|
|
1250
|
+
* to the system message or agent model requirements. It serves as a documentation mechanism
|
|
1251
|
+
* for developers to add explanatory comments, reminders, or annotations directly in the agent source.
|
|
1252
|
+
*
|
|
1253
|
+
* Key features:
|
|
1254
|
+
* - Makes no changes to the system message
|
|
1255
|
+
* - Makes no changes to agent model requirements
|
|
1256
|
+
* - Content is preserved in metadata.NOTE for debugging and inspection
|
|
1257
|
+
* - Multiple NOTE commitments are aggregated together
|
|
1258
|
+
* - Comments (# NOTE) are removed from the final system message
|
|
1259
|
+
*
|
|
1260
|
+
* Example usage in agent source:
|
|
1261
|
+
*
|
|
1262
|
+
* ```book
|
|
1263
|
+
* NOTE This agent was designed for customer support scenarios
|
|
1264
|
+
* NOTE Remember to update the knowledge base monthly
|
|
1265
|
+
* NOTE Performance optimized for quick response times
|
|
1266
|
+
* ```
|
|
1267
|
+
*
|
|
1268
|
+
* The above notes will be stored in metadata but won't affect the agent's behavior.
|
|
1269
|
+
*
|
|
1270
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1271
|
+
*/
|
|
1272
|
+
class NoteCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1273
|
+
constructor() {
|
|
1274
|
+
super('NOTE');
|
|
1275
|
+
}
|
|
1276
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1277
|
+
var _a;
|
|
1278
|
+
// The NOTE commitment makes no changes to the system message or model requirements
|
|
1279
|
+
// It only stores the note content in metadata for documentation purposes
|
|
1280
|
+
const trimmedContent = content.trim();
|
|
1281
|
+
if (!trimmedContent) {
|
|
1282
|
+
return requirements;
|
|
1283
|
+
}
|
|
1284
|
+
// Get existing note content from metadata
|
|
1285
|
+
const existingNoteContent = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.NOTE) || '';
|
|
1286
|
+
// Merge the new content with existing note content
|
|
1287
|
+
// When multiple NOTE commitments exist, they are aggregated together
|
|
1288
|
+
const mergedNoteContent = existingNoteContent ? `${existingNoteContent}\n${trimmedContent}` : trimmedContent;
|
|
1289
|
+
// Store the merged note content in metadata for debugging and inspection
|
|
1290
|
+
const updatedMetadata = {
|
|
1291
|
+
...requirements.metadata,
|
|
1292
|
+
NOTE: mergedNoteContent,
|
|
1293
|
+
};
|
|
1294
|
+
// Return requirements with updated metadata but no changes to system message
|
|
1295
|
+
return {
|
|
1296
|
+
...requirements,
|
|
1297
|
+
metadata: updatedMetadata,
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Singleton instance of the NOTE commitment definition
|
|
1303
|
+
*
|
|
1304
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1305
|
+
*/
|
|
1306
|
+
new NoteCommitmentDefinition();
|
|
1307
|
+
/**
|
|
1308
|
+
* [💞] Ignore a discrepancy between file name and entity name
|
|
1309
|
+
*/
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* PERSONA commitment definition
|
|
1313
|
+
*
|
|
1314
|
+
* The PERSONA commitment modifies the agent's personality and character in the system message.
|
|
1315
|
+
* It defines who the agent is, their background, expertise, and personality traits.
|
|
1316
|
+
*
|
|
1317
|
+
* Key features:
|
|
1318
|
+
* - Multiple PERSONA commitments are automatically merged into one
|
|
1319
|
+
* - Content is placed at the beginning of the system message
|
|
1320
|
+
* - Original content with comments is preserved in metadata.PERSONA
|
|
1321
|
+
* - Comments (# PERSONA) are removed from the final system message
|
|
1322
|
+
*
|
|
1323
|
+
* Example usage in agent source:
|
|
1324
|
+
*
|
|
1325
|
+
* ```book
|
|
1326
|
+
* PERSONA You are a helpful programming assistant with expertise in TypeScript and React
|
|
1327
|
+
* PERSONA You have deep knowledge of modern web development practices
|
|
1328
|
+
* ```
|
|
1329
|
+
*
|
|
1330
|
+
* The above will be merged into a single persona section at the beginning of the system message.
|
|
1331
|
+
*
|
|
1332
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1333
|
+
*/
|
|
1334
|
+
class PersonaCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1335
|
+
constructor() {
|
|
1336
|
+
super('PERSONA');
|
|
1337
|
+
}
|
|
1338
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1339
|
+
var _a, _b;
|
|
1340
|
+
// The PERSONA commitment aggregates all persona content and places it at the beginning
|
|
1341
|
+
const trimmedContent = content.trim();
|
|
1342
|
+
if (!trimmedContent) {
|
|
1343
|
+
return requirements;
|
|
1344
|
+
}
|
|
1345
|
+
// Get existing persona content from metadata
|
|
1346
|
+
const existingPersonaContent = ((_a = requirements.metadata) === null || _a === void 0 ? void 0 : _a.PERSONA) || '';
|
|
1347
|
+
// Merge the new content with existing persona content
|
|
1348
|
+
// When multiple PERSONA commitments exist, they are merged into one
|
|
1349
|
+
const mergedPersonaContent = existingPersonaContent
|
|
1350
|
+
? `${existingPersonaContent}\n${trimmedContent}`
|
|
1351
|
+
: trimmedContent;
|
|
1352
|
+
// Store the merged persona content in metadata for debugging and inspection
|
|
1353
|
+
const updatedMetadata = {
|
|
1354
|
+
...requirements.metadata,
|
|
1355
|
+
PERSONA: mergedPersonaContent,
|
|
1356
|
+
};
|
|
1357
|
+
// Get the agent name from metadata (which should contain the first line of agent source)
|
|
1358
|
+
// If not available, extract from current system message as fallback
|
|
1359
|
+
let agentName = (_b = requirements.metadata) === null || _b === void 0 ? void 0 : _b.agentName;
|
|
1360
|
+
if (!agentName) {
|
|
1361
|
+
// Fallback: extract from current system message
|
|
1362
|
+
const currentMessage = requirements.systemMessage.trim();
|
|
1363
|
+
const basicFormatMatch = currentMessage.match(/^You are (.+)$/);
|
|
1364
|
+
if (basicFormatMatch && basicFormatMatch[1]) {
|
|
1365
|
+
agentName = basicFormatMatch[1];
|
|
1366
|
+
}
|
|
1367
|
+
else {
|
|
1368
|
+
agentName = 'AI Agent'; // Final fallback
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
// Remove any existing persona content from the system message
|
|
1372
|
+
// (this handles the case where we're processing multiple PERSONA commitments)
|
|
1373
|
+
const currentMessage = requirements.systemMessage.trim();
|
|
1374
|
+
let cleanedMessage = currentMessage;
|
|
1375
|
+
// Check if current message starts with persona content or is just the basic format
|
|
1376
|
+
const basicFormatRegex = /^You are .+$/;
|
|
1377
|
+
const isBasicFormat = basicFormatRegex.test(currentMessage) && !currentMessage.includes('\n');
|
|
1378
|
+
if (isBasicFormat) {
|
|
1379
|
+
// Replace the basic format entirely
|
|
1380
|
+
cleanedMessage = '';
|
|
1381
|
+
}
|
|
1382
|
+
else if (currentMessage.startsWith('# PERSONA')) {
|
|
1383
|
+
// Remove existing persona section by finding where it ends
|
|
1384
|
+
const lines = currentMessage.split('\n');
|
|
1385
|
+
let personaEndIndex = lines.length;
|
|
1386
|
+
// Find the end of the PERSONA section (next comment or end of message)
|
|
1387
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1388
|
+
const line = lines[i].trim();
|
|
1389
|
+
if (line.startsWith('#') && !line.startsWith('# PERSONA')) {
|
|
1390
|
+
personaEndIndex = i;
|
|
1391
|
+
break;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
// Keep everything after the PERSONA section
|
|
1395
|
+
cleanedMessage = lines.slice(personaEndIndex).join('\n').trim();
|
|
1396
|
+
}
|
|
1397
|
+
// Create new system message with persona at the beginning
|
|
1398
|
+
// Format: "You are {agentName}\n{personaContent}"
|
|
1399
|
+
// The # PERSONA comment will be removed later by removeCommentsFromSystemMessage
|
|
1400
|
+
const personaSection = `# PERSONA\nYou are ${agentName}\n${mergedPersonaContent}`; // <- TODO: Use spaceTrim
|
|
1401
|
+
const newSystemMessage = cleanedMessage ? `${personaSection}\n\n${cleanedMessage}` : personaSection;
|
|
1402
|
+
return {
|
|
1403
|
+
...requirements,
|
|
1404
|
+
systemMessage: newSystemMessage,
|
|
1405
|
+
metadata: updatedMetadata,
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Singleton instance of the PERSONA commitment definition
|
|
1411
|
+
*
|
|
1412
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1413
|
+
*/
|
|
1414
|
+
new PersonaCommitmentDefinition();
|
|
1415
|
+
/**
|
|
1416
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1417
|
+
*/
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* RULE commitment definition
|
|
1421
|
+
*
|
|
1422
|
+
* The RULE/RULES commitment adds behavioral constraints and guidelines that the agent must follow.
|
|
1423
|
+
* These are specific instructions about what the agent should or shouldn't do.
|
|
1424
|
+
*
|
|
1425
|
+
* Example usage in agent source:
|
|
1426
|
+
*
|
|
1427
|
+
* ```book
|
|
1428
|
+
* RULE Always ask for clarification if the user's request is ambiguous
|
|
1429
|
+
* RULES Never provide medical advice, always refer to healthcare professionals
|
|
1430
|
+
* ```
|
|
1431
|
+
*
|
|
1432
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1433
|
+
*/
|
|
1434
|
+
class RuleCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1435
|
+
constructor(type = 'RULE') {
|
|
1436
|
+
super(type);
|
|
1437
|
+
}
|
|
1438
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1439
|
+
const trimmedContent = content.trim();
|
|
1440
|
+
if (!trimmedContent) {
|
|
1441
|
+
return requirements;
|
|
1442
|
+
}
|
|
1443
|
+
// Add rule to the system message
|
|
1444
|
+
const ruleSection = `Rule: ${trimmedContent}`;
|
|
1445
|
+
return this.appendToSystemMessage(requirements, ruleSection, '\n\n');
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Singleton instances of the RULE commitment definitions
|
|
1450
|
+
*
|
|
1451
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1452
|
+
*/
|
|
1453
|
+
new RuleCommitmentDefinition('RULE');
|
|
1454
|
+
/**
|
|
1455
|
+
* Singleton instances of the RULE commitment definitions
|
|
1456
|
+
*
|
|
1457
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1458
|
+
*/
|
|
1459
|
+
new RuleCommitmentDefinition('RULES');
|
|
1460
|
+
/**
|
|
1461
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1462
|
+
*/
|
|
1463
|
+
|
|
1464
|
+
/**
|
|
1465
|
+
* SAMPLE commitment definition
|
|
1466
|
+
*
|
|
1467
|
+
* The SAMPLE/EXAMPLE commitment provides examples of how the agent should respond
|
|
1468
|
+
* or behave in certain situations. These examples help guide the agent's responses.
|
|
1469
|
+
*
|
|
1470
|
+
* Example usage in agent source:
|
|
1471
|
+
*
|
|
1472
|
+
* ```book
|
|
1473
|
+
* SAMPLE When asked about pricing, respond: "Our basic plan starts at $10/month..."
|
|
1474
|
+
* EXAMPLE For code questions, always include working code snippets
|
|
1475
|
+
* ```
|
|
1476
|
+
*
|
|
1477
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1478
|
+
*/
|
|
1479
|
+
class SampleCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1480
|
+
constructor(type = 'SAMPLE') {
|
|
1481
|
+
super(type);
|
|
1482
|
+
}
|
|
1483
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1484
|
+
const trimmedContent = content.trim();
|
|
1485
|
+
if (!trimmedContent) {
|
|
1486
|
+
return requirements;
|
|
1487
|
+
}
|
|
1488
|
+
// Add example to the system message
|
|
1489
|
+
const exampleSection = `Example: ${trimmedContent}`;
|
|
1490
|
+
return this.appendToSystemMessage(requirements, exampleSection, '\n\n');
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Singleton instances of the SAMPLE commitment definitions
|
|
1495
|
+
*
|
|
1496
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1497
|
+
*/
|
|
1498
|
+
new SampleCommitmentDefinition('SAMPLE');
|
|
1499
|
+
/**
|
|
1500
|
+
* Singleton instances of the SAMPLE commitment definitions
|
|
1501
|
+
*
|
|
1502
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1503
|
+
*/
|
|
1504
|
+
new SampleCommitmentDefinition('EXAMPLE');
|
|
1505
|
+
/**
|
|
1506
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1507
|
+
*/
|
|
1508
|
+
|
|
1509
|
+
/**
|
|
1510
|
+
* STYLE commitment definition
|
|
1511
|
+
*
|
|
1512
|
+
* The STYLE commitment defines how the agent should format and present its responses.
|
|
1513
|
+
* This includes tone, writing style, formatting preferences, and communication patterns.
|
|
1514
|
+
*
|
|
1515
|
+
* Example usage in agent source:
|
|
1516
|
+
*
|
|
1517
|
+
* ```book
|
|
1518
|
+
* STYLE Write in a professional but friendly tone, use bullet points for lists
|
|
1519
|
+
* STYLE Always provide code examples when explaining programming concepts
|
|
1520
|
+
* ```
|
|
1521
|
+
*
|
|
1522
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1523
|
+
*/
|
|
1524
|
+
class StyleCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1525
|
+
constructor() {
|
|
1526
|
+
super('STYLE');
|
|
1527
|
+
}
|
|
1528
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1529
|
+
const trimmedContent = content.trim();
|
|
1530
|
+
if (!trimmedContent) {
|
|
1531
|
+
return requirements;
|
|
1532
|
+
}
|
|
1533
|
+
// Add style instructions to the system message
|
|
1534
|
+
const styleSection = `Style: ${trimmedContent}`;
|
|
1535
|
+
return this.appendToSystemMessage(requirements, styleSection, '\n\n');
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Singleton instance of the STYLE commitment definition
|
|
1540
|
+
*
|
|
1541
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
1542
|
+
*/
|
|
1543
|
+
new StyleCommitmentDefinition();
|
|
1544
|
+
/**
|
|
1545
|
+
* [💞] Ignore a discrepancy between file name and entity name
|
|
1546
|
+
*/
|
|
1547
|
+
|
|
1548
|
+
/**
|
|
1549
|
+
* Placeholder commitment definition for commitments that are not yet implemented
|
|
1550
|
+
*
|
|
1551
|
+
* This commitment simply adds its content 1:1 into the system message,
|
|
1552
|
+
* preserving the original behavior until proper implementation is added.
|
|
1553
|
+
*
|
|
1554
|
+
* @public exported from `@promptbook/core`
|
|
1555
|
+
*/
|
|
1556
|
+
class NotYetImplementedCommitmentDefinition extends BaseCommitmentDefinition {
|
|
1557
|
+
constructor(type) {
|
|
1558
|
+
super(type);
|
|
1559
|
+
}
|
|
1560
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
1561
|
+
const trimmedContent = content.trim();
|
|
1562
|
+
if (!trimmedContent) {
|
|
1563
|
+
return requirements;
|
|
1564
|
+
}
|
|
1565
|
+
// Add the commitment content 1:1 to the system message
|
|
1566
|
+
const commitmentLine = `${this.type} ${trimmedContent}`;
|
|
1567
|
+
return this.appendToSystemMessage(requirements, commitmentLine, '\n\n');
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// Import all commitment definition classes
|
|
1572
|
+
/**
|
|
1573
|
+
* Registry of all available commitment definitions
|
|
1574
|
+
* This array contains instances of all commitment definitions
|
|
1575
|
+
* This is the single source of truth for all commitments in the system
|
|
1576
|
+
*
|
|
1577
|
+
* @private Use functions to access commitments instead of this array directly
|
|
1578
|
+
*/
|
|
1579
|
+
const COMMITMENT_REGISTRY = [
|
|
1580
|
+
// Fully implemented commitments
|
|
1581
|
+
new PersonaCommitmentDefinition(),
|
|
1582
|
+
new KnowledgeCommitmentDefinition(),
|
|
1583
|
+
new StyleCommitmentDefinition(),
|
|
1584
|
+
new RuleCommitmentDefinition('RULE'),
|
|
1585
|
+
new RuleCommitmentDefinition('RULES'),
|
|
1586
|
+
new SampleCommitmentDefinition('SAMPLE'),
|
|
1587
|
+
new SampleCommitmentDefinition('EXAMPLE'),
|
|
1588
|
+
new FormatCommitmentDefinition(),
|
|
1589
|
+
new ModelCommitmentDefinition(),
|
|
1590
|
+
new ActionCommitmentDefinition(),
|
|
1591
|
+
new MetaImageCommitmentDefinition(),
|
|
1592
|
+
new MetaLinkCommitmentDefinition(),
|
|
1593
|
+
new NoteCommitmentDefinition(),
|
|
1594
|
+
// Not yet implemented commitments (using placeholder)
|
|
1595
|
+
new NotYetImplementedCommitmentDefinition('EXPECT'),
|
|
1596
|
+
new NotYetImplementedCommitmentDefinition('SCENARIO'),
|
|
1597
|
+
new NotYetImplementedCommitmentDefinition('SCENARIOS'),
|
|
1598
|
+
new NotYetImplementedCommitmentDefinition('BEHAVIOUR'),
|
|
1599
|
+
new NotYetImplementedCommitmentDefinition('BEHAVIOURS'),
|
|
1600
|
+
new NotYetImplementedCommitmentDefinition('AVOID'),
|
|
1601
|
+
new NotYetImplementedCommitmentDefinition('AVOIDANCE'),
|
|
1602
|
+
new NotYetImplementedCommitmentDefinition('GOAL'),
|
|
1603
|
+
new NotYetImplementedCommitmentDefinition('GOALS'),
|
|
1604
|
+
new NotYetImplementedCommitmentDefinition('CONTEXT'),
|
|
1605
|
+
];
|
|
1606
|
+
/**
|
|
1607
|
+
* Gets a commitment definition by its type
|
|
1608
|
+
* @param type The commitment type to look up
|
|
1609
|
+
* @returns The commitment definition or null if not found
|
|
1610
|
+
*
|
|
1611
|
+
* @public exported from `@promptbook/core`
|
|
1612
|
+
*/
|
|
1613
|
+
function getCommitmentDefinition(type) {
|
|
1614
|
+
return COMMITMENT_REGISTRY.find((commitmentDefinition) => commitmentDefinition.type === type) || null;
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Gets all available commitment definitions
|
|
1618
|
+
* @returns Array of all commitment definitions
|
|
1619
|
+
*
|
|
1620
|
+
* @public exported from `@promptbook/core`
|
|
1621
|
+
*/
|
|
1622
|
+
function getAllCommitmentDefinitions() {
|
|
1623
|
+
return $deepFreeze([...COMMITMENT_REGISTRY]);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Gets all available commitment types
|
|
1627
|
+
* @returns Array of all commitment types
|
|
1628
|
+
*
|
|
1629
|
+
* @public exported from `@promptbook/core`
|
|
1630
|
+
*/
|
|
1631
|
+
function getAllCommitmentTypes() {
|
|
1632
|
+
return $deepFreeze(COMMITMENT_REGISTRY.map((commitmentDefinition) => commitmentDefinition.type));
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Checks if a commitment type is supported
|
|
1636
|
+
* @param type The commitment type to check
|
|
1637
|
+
* @returns True if the commitment type is supported
|
|
1638
|
+
*
|
|
1639
|
+
* @public exported from `@promptbook/core`
|
|
1640
|
+
*/
|
|
1641
|
+
function isCommitmentSupported(type) {
|
|
1642
|
+
return COMMITMENT_REGISTRY.some((commitmentDefinition) => commitmentDefinition.type === type);
|
|
1643
|
+
}
|
|
1644
|
+
/**
|
|
1645
|
+
* TODO: !!!! Maybe create through standardized $register
|
|
1646
|
+
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
1647
|
+
*/
|
|
1648
|
+
|
|
1649
|
+
/**
|
|
1650
|
+
* Parses agent source using the new commitment system with multiline support
|
|
1651
|
+
* This function replaces the hardcoded commitment parsing in the original parseAgentSource
|
|
1652
|
+
*
|
|
1653
|
+
* @private
|
|
1654
|
+
*/
|
|
1655
|
+
function parseAgentSourceWithCommitments(agentSource) {
|
|
1656
|
+
var _a, _b, _c;
|
|
1657
|
+
if (!agentSource || !agentSource.trim()) {
|
|
1658
|
+
return {
|
|
1659
|
+
agentName: null,
|
|
1660
|
+
commitments: [],
|
|
1661
|
+
nonCommitmentLines: [],
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
const lines = agentSource.split('\n');
|
|
1665
|
+
const agentName = (((_a = lines[0]) === null || _a === void 0 ? void 0 : _a.trim()) || null);
|
|
1666
|
+
const commitments = [];
|
|
1667
|
+
const nonCommitmentLines = [];
|
|
1668
|
+
// Always add the first line (agent name) to non-commitment lines
|
|
1669
|
+
if (lines[0] !== undefined) {
|
|
1670
|
+
nonCommitmentLines.push(lines[0]);
|
|
1671
|
+
}
|
|
1672
|
+
// Parse commitments with multiline support
|
|
1673
|
+
let currentCommitment = null;
|
|
1674
|
+
// Process lines starting from the second line (skip agent name)
|
|
1675
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1676
|
+
const line = lines[i];
|
|
1677
|
+
if (line === undefined) {
|
|
1678
|
+
continue;
|
|
1679
|
+
}
|
|
1680
|
+
// Check if this line starts a new commitment
|
|
1681
|
+
let foundNewCommitment = false;
|
|
1682
|
+
for (const definition of COMMITMENT_REGISTRY) {
|
|
1683
|
+
const typeRegex = definition.createTypeRegex();
|
|
1684
|
+
const match = typeRegex.exec(line.trim());
|
|
1685
|
+
if (match && ((_b = match.groups) === null || _b === void 0 ? void 0 : _b.type)) {
|
|
1686
|
+
// Save the previous commitment if it exists
|
|
1687
|
+
if (currentCommitment) {
|
|
1688
|
+
const fullContent = currentCommitment.contentLines.join('\n');
|
|
1689
|
+
commitments.push({
|
|
1690
|
+
type: currentCommitment.type,
|
|
1691
|
+
content: spaceTrim.spaceTrim(fullContent),
|
|
1692
|
+
originalLine: currentCommitment.originalStartLine,
|
|
1693
|
+
lineNumber: currentCommitment.startLineNumber,
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
// Extract the initial content from the commitment line
|
|
1697
|
+
const fullRegex = definition.createRegex();
|
|
1698
|
+
const fullMatch = fullRegex.exec(line.trim());
|
|
1699
|
+
const initialContent = ((_c = fullMatch === null || fullMatch === void 0 ? void 0 : fullMatch.groups) === null || _c === void 0 ? void 0 : _c.contents) || '';
|
|
1700
|
+
// Start a new commitment
|
|
1701
|
+
currentCommitment = {
|
|
1702
|
+
type: definition.type,
|
|
1703
|
+
startLineNumber: i + 1,
|
|
1704
|
+
originalStartLine: line,
|
|
1705
|
+
contentLines: initialContent ? [initialContent] : [],
|
|
1706
|
+
};
|
|
1707
|
+
foundNewCommitment = true;
|
|
1708
|
+
break;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
if (!foundNewCommitment) {
|
|
1712
|
+
if (currentCommitment) {
|
|
1713
|
+
// This line belongs to the current commitment
|
|
1714
|
+
currentCommitment.contentLines.push(line);
|
|
1715
|
+
}
|
|
1716
|
+
else {
|
|
1717
|
+
// This line is not part of any commitment
|
|
1718
|
+
nonCommitmentLines.push(line);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
// Don't forget to save the last commitment if it exists
|
|
1723
|
+
if (currentCommitment) {
|
|
1724
|
+
const fullContent = currentCommitment.contentLines.join('\n');
|
|
1725
|
+
commitments.push({
|
|
1726
|
+
type: currentCommitment.type,
|
|
1727
|
+
content: spaceTrim.spaceTrim(fullContent),
|
|
1728
|
+
originalLine: currentCommitment.originalStartLine,
|
|
1729
|
+
lineNumber: currentCommitment.startLineNumber,
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
return {
|
|
1733
|
+
agentName,
|
|
1734
|
+
commitments,
|
|
1735
|
+
nonCommitmentLines,
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Extracts basic information from agent source using the new commitment system
|
|
1740
|
+
* This maintains compatibility with the original parseAgentSource interface
|
|
1741
|
+
*
|
|
1742
|
+
* @private
|
|
1743
|
+
*/
|
|
1744
|
+
function parseAgentSourceBasicInfo(agentSource) {
|
|
1745
|
+
const parseResult = parseAgentSourceWithCommitments(agentSource);
|
|
1746
|
+
// Find PERSONA and META IMAGE commitments
|
|
1747
|
+
let personaDescription = null;
|
|
1748
|
+
let profileImageUrl;
|
|
1749
|
+
for (const commitment of parseResult.commitments) {
|
|
1750
|
+
if (commitment.type === 'PERSONA' && !personaDescription) {
|
|
1751
|
+
personaDescription = commitment.content;
|
|
1752
|
+
}
|
|
1753
|
+
else if (commitment.type === 'META IMAGE' && !profileImageUrl) {
|
|
1754
|
+
profileImageUrl = commitment.content;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
// Generate gravatar fallback if no profile image specified
|
|
1758
|
+
if (!profileImageUrl) {
|
|
1759
|
+
profileImageUrl = generateGravatarUrl(parseResult.agentName);
|
|
1760
|
+
}
|
|
1761
|
+
return {
|
|
1762
|
+
agentName: parseResult.agentName,
|
|
1763
|
+
personaDescription,
|
|
1764
|
+
profileImageUrl,
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/**
|
|
1769
|
+
* Parses agent source string into its components
|
|
1770
|
+
*/
|
|
1771
|
+
// Cache for parsed agent sources to prevent repeated parsing
|
|
1772
|
+
const parsedAgentSourceCache = new Map();
|
|
1773
|
+
/**
|
|
1774
|
+
* Parses basic information from agent source
|
|
1775
|
+
*
|
|
1776
|
+
* There are 2 similar functions:
|
|
1777
|
+
* - `parseAgentSource` which is a lightweight parser for agent source, it parses basic information and its purpose is to be quick and synchronous. The commitments there are hardcoded.
|
|
1778
|
+
* - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronously.
|
|
1779
|
+
*
|
|
1780
|
+
* @public exported from `@promptbook/core`
|
|
1781
|
+
*/
|
|
1782
|
+
function parseAgentSource(agentSource) {
|
|
1783
|
+
// Check if we already parsed this agent source
|
|
1784
|
+
if (parsedAgentSourceCache.has(agentSource)) {
|
|
1785
|
+
return parsedAgentSourceCache.get(agentSource);
|
|
1786
|
+
}
|
|
1787
|
+
// Use the new commitment-based parsing system
|
|
1788
|
+
const result = parseAgentSourceBasicInfo(agentSource);
|
|
1789
|
+
// Cache the result
|
|
1790
|
+
parsedAgentSourceCache.set(agentSource, result);
|
|
1791
|
+
return result;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* Type guard to check if a string is a valid agent source
|
|
1796
|
+
*
|
|
1797
|
+
* @public exported from `@promptbook/core`
|
|
1798
|
+
*/
|
|
1799
|
+
function isValidBook(value) {
|
|
1800
|
+
// Basic validation - agent source should have at least a name (first line)
|
|
1801
|
+
return typeof value === 'string' /* && value.trim().length > 0 */;
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* Validates and converts a string to agent source branded type
|
|
1805
|
+
* This function should be used when you have a string that you know represents agent source
|
|
1806
|
+
* but need to convert it to the branded type for type safety
|
|
1807
|
+
*
|
|
1808
|
+
* @public exported from `@promptbook/core`
|
|
1809
|
+
*/
|
|
1810
|
+
function validateBook(source) {
|
|
1811
|
+
if (!isValidBook(source)) {
|
|
1812
|
+
throw new Error('Invalid agent source: must be a string');
|
|
1813
|
+
}
|
|
1814
|
+
return source;
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Default book
|
|
1818
|
+
*
|
|
1819
|
+
* @public exported from `@promptbook/core`
|
|
1820
|
+
*/
|
|
1821
|
+
const DEFAULT_BOOK = validateBook(spaceTrim__default["default"](`
|
|
1822
|
+
AI Avatar
|
|
1823
|
+
|
|
1824
|
+
PERSONA A friendly AI assistant that helps you with your tasks
|
|
1825
|
+
`));
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* Creates an empty/basic agent model requirements object
|
|
1829
|
+
* This serves as the starting point for the reduce-like pattern
|
|
1830
|
+
* where each commitment applies its changes to build the final requirements
|
|
1831
|
+
*
|
|
1832
|
+
* @public exported from `@promptbook/core`
|
|
1833
|
+
*/
|
|
1834
|
+
function createEmptyAgentModelRequirements() {
|
|
1835
|
+
return {
|
|
1836
|
+
systemMessage: '',
|
|
1837
|
+
modelName: '!!!!DEFAULT_MODEL_ID',
|
|
1838
|
+
temperature: 0.7,
|
|
1839
|
+
topP: 0.9,
|
|
1840
|
+
topK: 50,
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Creates a basic agent model requirements with just the agent name
|
|
1845
|
+
* This is used when we have an agent name but no commitments
|
|
1846
|
+
*
|
|
1847
|
+
* @public exported from `@promptbook/core`
|
|
1848
|
+
*/
|
|
1849
|
+
function createBasicAgentModelRequirements(agentName) {
|
|
1850
|
+
const empty = createEmptyAgentModelRequirements();
|
|
1851
|
+
return {
|
|
1852
|
+
...empty,
|
|
1853
|
+
systemMessage: `You are ${agentName || 'AI Agent'}`,
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
/**
|
|
1857
|
+
* TODO: !!!! Deduplicate model requirements
|
|
1858
|
+
*/
|
|
1859
|
+
|
|
1860
|
+
/**
|
|
1861
|
+
* Removes comment lines (lines starting with #) from a system message
|
|
1862
|
+
* This is used to clean up the final system message before sending it to the AI model
|
|
1863
|
+
* while preserving the original content with comments in metadata
|
|
1864
|
+
*
|
|
1865
|
+
* @param systemMessage The system message that may contain comment lines
|
|
1866
|
+
* @returns The system message with comment lines removed
|
|
1867
|
+
*
|
|
1868
|
+
* @private - TODO: [🧠] Maybe should be public?
|
|
1869
|
+
*/
|
|
1870
|
+
function removeCommentsFromSystemMessage(systemMessage) {
|
|
1871
|
+
if (!systemMessage) {
|
|
1872
|
+
return systemMessage;
|
|
1873
|
+
}
|
|
1874
|
+
const lines = systemMessage.split('\n');
|
|
1875
|
+
const filteredLines = lines.filter((line) => {
|
|
1876
|
+
const trimmedLine = line.trim();
|
|
1877
|
+
// Remove lines that start with # (comments)
|
|
1878
|
+
return !trimmedLine.startsWith('#');
|
|
1879
|
+
});
|
|
1880
|
+
return filteredLines.join('\n').trim();
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
/**
|
|
1884
|
+
* Creates agent model requirements using the new commitment system
|
|
1885
|
+
* This function uses a reduce-like pattern where each commitment applies its changes
|
|
1886
|
+
* to build the final requirements starting from a basic empty model
|
|
1887
|
+
*
|
|
1888
|
+
* @private
|
|
1889
|
+
*/
|
|
1890
|
+
async function createAgentModelRequirementsWithCommitments(agentSource, modelName) {
|
|
1891
|
+
// Parse the agent source to extract commitments
|
|
1892
|
+
const parseResult = parseAgentSourceWithCommitments(agentSource);
|
|
1893
|
+
// Start with basic agent model requirements
|
|
1894
|
+
let requirements = createBasicAgentModelRequirements(parseResult.agentName);
|
|
1895
|
+
// Store the agent name in metadata so commitments can access it
|
|
1896
|
+
requirements = {
|
|
1897
|
+
...requirements,
|
|
1898
|
+
metadata: {
|
|
1899
|
+
...requirements.metadata,
|
|
1900
|
+
agentName: parseResult.agentName,
|
|
1901
|
+
},
|
|
1902
|
+
};
|
|
1903
|
+
// Override model name if provided
|
|
1904
|
+
if (modelName) {
|
|
1905
|
+
requirements = {
|
|
1906
|
+
...requirements,
|
|
1907
|
+
modelName,
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
// Apply each commitment in order using reduce-like pattern
|
|
1911
|
+
for (const commitment of parseResult.commitments) {
|
|
1912
|
+
const definition = getCommitmentDefinition(commitment.type);
|
|
1913
|
+
if (definition) {
|
|
1914
|
+
try {
|
|
1915
|
+
requirements = definition.applyToAgentModelRequirements(requirements, commitment.content);
|
|
1916
|
+
}
|
|
1917
|
+
catch (error) {
|
|
1918
|
+
console.warn(`Failed to apply commitment ${commitment.type}:`, error);
|
|
1919
|
+
// Continue with other commitments even if one fails
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
// Handle MCP servers (extract from original agent source)
|
|
1924
|
+
const mcpServers = extractMcpServers(agentSource);
|
|
1925
|
+
if (mcpServers.length > 0) {
|
|
1926
|
+
requirements = {
|
|
1927
|
+
...requirements,
|
|
1928
|
+
mcpServers,
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
// Add non-commitment lines to system message if they exist
|
|
1932
|
+
const nonCommitmentContent = parseResult.nonCommitmentLines
|
|
1933
|
+
.filter((line, index) => index > 0 || !parseResult.agentName) // Skip first line if it's the agent name
|
|
1934
|
+
.filter((line) => line.trim()) // Remove empty lines
|
|
1935
|
+
.join('\n')
|
|
1936
|
+
.trim();
|
|
1937
|
+
if (nonCommitmentContent) {
|
|
1938
|
+
requirements = {
|
|
1939
|
+
...requirements,
|
|
1940
|
+
systemMessage: requirements.systemMessage + '\n\n' + nonCommitmentContent,
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
// Remove comment lines (lines starting with #) from the final system message
|
|
1944
|
+
// while preserving the original content with comments in metadata
|
|
1945
|
+
const cleanedSystemMessage = removeCommentsFromSystemMessage(requirements.systemMessage);
|
|
1946
|
+
return {
|
|
1947
|
+
...requirements,
|
|
1948
|
+
systemMessage: cleanedSystemMessage,
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* Cache for expensive createAgentModelRequirementsWithCommitments calls
|
|
1953
|
+
* @private
|
|
1954
|
+
*/
|
|
1955
|
+
const modelRequirementsCache = new Map();
|
|
1956
|
+
/**
|
|
1957
|
+
* @private - TODO: Maybe should be public
|
|
1958
|
+
*/
|
|
1959
|
+
const CACHE_SIZE_LIMIT = 100;
|
|
1960
|
+
/**
|
|
1961
|
+
* Cached version of createAgentModelRequirementsWithCommitments
|
|
1962
|
+
* This maintains the same caching behavior as the original function
|
|
1963
|
+
*
|
|
1964
|
+
* @private
|
|
1965
|
+
*/
|
|
1966
|
+
async function createAgentModelRequirementsWithCommitmentsCached(agentSource, modelName) {
|
|
1967
|
+
// Create cache key
|
|
1968
|
+
const cacheKey = `${agentSource}|${modelName || 'default'}`;
|
|
1969
|
+
// Check cache first
|
|
1970
|
+
if (modelRequirementsCache.has(cacheKey)) {
|
|
1971
|
+
return modelRequirementsCache.get(cacheKey);
|
|
1972
|
+
}
|
|
1973
|
+
// Limit cache size to prevent memory leaks
|
|
1974
|
+
if (modelRequirementsCache.size >= CACHE_SIZE_LIMIT) {
|
|
1975
|
+
const firstKey = modelRequirementsCache.keys().next().value;
|
|
1976
|
+
if (firstKey) {
|
|
1977
|
+
modelRequirementsCache.delete(firstKey);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
// Create requirements
|
|
1981
|
+
const requirements = await createAgentModelRequirementsWithCommitments(agentSource, modelName);
|
|
1982
|
+
// Cache the result
|
|
1983
|
+
modelRequirementsCache.set(cacheKey, requirements);
|
|
1984
|
+
return requirements;
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
// TODO: Remove or use:
|
|
1988
|
+
//const CACHE_SIZE_LIMIT = 100; // Prevent memory leaks by limiting cache size
|
|
1989
|
+
/**
|
|
1990
|
+
* Creates model requirements for an agent based on its source
|
|
1991
|
+
* Results are cached to improve performance for repeated calls with the same agentSource and modelName
|
|
1992
|
+
*
|
|
1993
|
+
* There are 2 similar functions:
|
|
1994
|
+
* - `parseAgentSource` which is a lightweight parser for agent source, it parses basic information and its purpose is to be quick and synchronous. The commitments there are hardcoded.
|
|
1995
|
+
* - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronously.
|
|
557
1996
|
*
|
|
558
1997
|
* @public exported from `@promptbook/core`
|
|
559
1998
|
*/
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
Object.setPrototypeOf(this, UnexpectedError.prototype);
|
|
1999
|
+
async function createAgentModelRequirements(agentSource, modelName = '!!!!DEFAULT_MODEL_ID') {
|
|
2000
|
+
// Use the new commitment-based system
|
|
2001
|
+
return createAgentModelRequirementsWithCommitmentsCached(agentSource, modelName);
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Extracts MCP servers from agent source
|
|
2005
|
+
*
|
|
2006
|
+
* @param agentSource The agent source string that may contain MCP lines
|
|
2007
|
+
* @returns Array of MCP server identifiers
|
|
2008
|
+
*
|
|
2009
|
+
* @private TODO: [🧠] Maybe should be public
|
|
2010
|
+
*/
|
|
2011
|
+
function extractMcpServers(agentSource) {
|
|
2012
|
+
if (!agentSource) {
|
|
2013
|
+
return [];
|
|
576
2014
|
}
|
|
2015
|
+
const lines = agentSource.split('\n');
|
|
2016
|
+
const mcpRegex = /^\s*MCP\s+(.+)$/i;
|
|
2017
|
+
const mcpServers = [];
|
|
2018
|
+
// Look for MCP lines
|
|
2019
|
+
for (const line of lines) {
|
|
2020
|
+
const match = line.match(mcpRegex);
|
|
2021
|
+
if (match && match[1]) {
|
|
2022
|
+
mcpServers.push(match[1].trim());
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
return mcpServers;
|
|
577
2026
|
}
|
|
578
2027
|
|
|
579
2028
|
/**
|
|
580
|
-
*
|
|
2029
|
+
* Converts PipelineCollection to serialized JSON
|
|
2030
|
+
*
|
|
2031
|
+
* Note: Functions `collectionToJson` and `createCollectionFromJson` are complementary
|
|
581
2032
|
*
|
|
582
2033
|
* @public exported from `@promptbook/core`
|
|
583
2034
|
*/
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
2035
|
+
async function collectionToJson(collection) {
|
|
2036
|
+
const pipelineUrls = await collection.listPipelines();
|
|
2037
|
+
const promptbooks = await Promise.all(pipelineUrls.map((url) => collection.getPipelineByUrl(url)));
|
|
2038
|
+
return promptbooks;
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* TODO: [🧠] Maybe clear `sourceFile` or clear when exposing through API or remote server
|
|
2042
|
+
*/
|
|
590
2043
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
2044
|
+
/**
|
|
2045
|
+
* Checks if value is valid email
|
|
2046
|
+
*
|
|
2047
|
+
* @public exported from `@promptbook/utils`
|
|
2048
|
+
*/
|
|
2049
|
+
function isValidEmail(email) {
|
|
2050
|
+
if (typeof email !== 'string') {
|
|
2051
|
+
return false;
|
|
2052
|
+
}
|
|
2053
|
+
if (email.split('\n').length > 1) {
|
|
2054
|
+
return false;
|
|
596
2055
|
}
|
|
2056
|
+
return /^.+@.+\..+$/.test(email);
|
|
597
2057
|
}
|
|
598
2058
|
|
|
599
2059
|
/**
|
|
600
|
-
*
|
|
2060
|
+
* Tests if given string is valid URL.
|
|
601
2061
|
*
|
|
602
|
-
*
|
|
603
|
-
* @
|
|
604
|
-
|
|
2062
|
+
* Note: This does not check if the file exists only if the path is valid
|
|
2063
|
+
* @public exported from `@promptbook/utils`
|
|
2064
|
+
*/
|
|
2065
|
+
function isValidFilePath(filename) {
|
|
2066
|
+
if (typeof filename !== 'string') {
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
if (filename.split('\n').length > 1) {
|
|
2070
|
+
return false;
|
|
2071
|
+
}
|
|
2072
|
+
if (filename.split(' ').length >
|
|
2073
|
+
5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
|
|
2074
|
+
return false;
|
|
2075
|
+
}
|
|
2076
|
+
const filenameSlashes = filename.split('\\').join('/');
|
|
2077
|
+
// Absolute Unix path: /hello.txt
|
|
2078
|
+
if (/^(\/)/i.test(filenameSlashes)) {
|
|
2079
|
+
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
2080
|
+
return true;
|
|
2081
|
+
}
|
|
2082
|
+
// Absolute Windows path: /hello.txt
|
|
2083
|
+
if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
|
|
2084
|
+
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
2085
|
+
return true;
|
|
2086
|
+
}
|
|
2087
|
+
// Relative path: ./hello.txt
|
|
2088
|
+
if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
|
|
2089
|
+
// console.log(filename, 'Relative path: ./hello.txt');
|
|
2090
|
+
return true;
|
|
2091
|
+
}
|
|
2092
|
+
// Allow paths like foo/hello
|
|
2093
|
+
if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
|
|
2094
|
+
// console.log(filename, 'Allow paths like foo/hello');
|
|
2095
|
+
return true;
|
|
2096
|
+
}
|
|
2097
|
+
// Allow paths like hello.book
|
|
2098
|
+
if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
|
|
2099
|
+
// console.log(filename, 'Allow paths like hello.book');
|
|
2100
|
+
return true;
|
|
2101
|
+
}
|
|
2102
|
+
return false;
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* TODO: [🍏] Implement for MacOs
|
|
2106
|
+
*/
|
|
2107
|
+
|
|
2108
|
+
/**
|
|
2109
|
+
* Tests if given string is valid URL.
|
|
605
2110
|
*
|
|
606
|
-
*
|
|
2111
|
+
* Note: Dataurl are considered perfectly valid.
|
|
2112
|
+
* Note: There are two similar functions:
|
|
2113
|
+
* - `isValidUrl` which tests any URL
|
|
2114
|
+
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
2115
|
+
*
|
|
2116
|
+
* @public exported from `@promptbook/utils`
|
|
607
2117
|
*/
|
|
608
|
-
function
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const wrappedError = whatWasThrown;
|
|
612
|
-
throw wrappedError;
|
|
2118
|
+
function isValidUrl(url) {
|
|
2119
|
+
if (typeof url !== 'string') {
|
|
2120
|
+
return false;
|
|
613
2121
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
2122
|
+
try {
|
|
2123
|
+
if (url.startsWith('blob:')) {
|
|
2124
|
+
url = url.replace(/^blob:/, '');
|
|
2125
|
+
}
|
|
2126
|
+
const urlObject = new URL(url /* because fail is handled */);
|
|
2127
|
+
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
2128
|
+
return false;
|
|
2129
|
+
}
|
|
2130
|
+
return true;
|
|
618
2131
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
2132
|
+
catch (error) {
|
|
2133
|
+
return false;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
/**
|
|
2138
|
+
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
2139
|
+
*
|
|
2140
|
+
* @public exported from `@promptbook/core`
|
|
2141
|
+
*/
|
|
2142
|
+
class ParseError extends Error {
|
|
2143
|
+
constructor(message) {
|
|
2144
|
+
super(message);
|
|
2145
|
+
this.name = 'ParseError';
|
|
2146
|
+
Object.setPrototypeOf(this, ParseError.prototype);
|
|
622
2147
|
}
|
|
623
|
-
// Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
|
|
624
|
-
throw new WrappedError(whatWasThrown);
|
|
625
2148
|
}
|
|
2149
|
+
/**
|
|
2150
|
+
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
2151
|
+
*/
|
|
626
2152
|
|
|
627
2153
|
/**
|
|
628
2154
|
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
@@ -873,33 +2399,6 @@
|
|
|
873
2399
|
return orderedValue;
|
|
874
2400
|
}
|
|
875
2401
|
|
|
876
|
-
/**
|
|
877
|
-
* Freezes the given object and all its nested objects recursively
|
|
878
|
-
*
|
|
879
|
-
* Note: `$` is used to indicate that this function is not a pure function - it mutates given object
|
|
880
|
-
* Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
|
|
881
|
-
*
|
|
882
|
-
* @returns The same object as the input, but deeply frozen
|
|
883
|
-
* @public exported from `@promptbook/utils`
|
|
884
|
-
*/
|
|
885
|
-
function $deepFreeze(objectValue) {
|
|
886
|
-
if (Array.isArray(objectValue)) {
|
|
887
|
-
return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
|
|
888
|
-
}
|
|
889
|
-
const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
890
|
-
for (const propertyName of propertyNames) {
|
|
891
|
-
const value = objectValue[propertyName];
|
|
892
|
-
if (value && typeof value === 'object') {
|
|
893
|
-
$deepFreeze(value);
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
Object.freeze(objectValue);
|
|
897
|
-
return objectValue;
|
|
898
|
-
}
|
|
899
|
-
/**
|
|
900
|
-
* TODO: [🧠] Is there a way how to meaningfully test this utility
|
|
901
|
-
*/
|
|
902
|
-
|
|
903
2402
|
/**
|
|
904
2403
|
* Checks if the value is [🚉] serializable as JSON
|
|
905
2404
|
* If not, throws an UnexpectedError with a rich error message and tracking
|
|
@@ -2121,19 +3620,6 @@
|
|
|
2121
3620
|
* TODO: [🧠][🌂] Add id to all errors
|
|
2122
3621
|
*/
|
|
2123
3622
|
|
|
2124
|
-
/**
|
|
2125
|
-
* Error thrown when a fetch request fails
|
|
2126
|
-
*
|
|
2127
|
-
* @public exported from `@promptbook/core`
|
|
2128
|
-
*/
|
|
2129
|
-
class PromptbookFetchError extends Error {
|
|
2130
|
-
constructor(message) {
|
|
2131
|
-
super(message);
|
|
2132
|
-
this.name = 'PromptbookFetchError';
|
|
2133
|
-
Object.setPrototypeOf(this, PromptbookFetchError.prototype);
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
3623
|
/**
|
|
2138
3624
|
* Index of all custom errors
|
|
2139
3625
|
*
|
|
@@ -5835,37 +7321,6 @@
|
|
|
5835
7321
|
return value;
|
|
5836
7322
|
}
|
|
5837
7323
|
|
|
5838
|
-
/**
|
|
5839
|
-
* The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
|
|
5840
|
-
*
|
|
5841
|
-
* @public exported from `@promptbook/core`
|
|
5842
|
-
*/
|
|
5843
|
-
const promptbookFetch = async (urlOrRequest, init) => {
|
|
5844
|
-
try {
|
|
5845
|
-
return await fetch(urlOrRequest, init);
|
|
5846
|
-
}
|
|
5847
|
-
catch (error) {
|
|
5848
|
-
assertsError(error);
|
|
5849
|
-
let url;
|
|
5850
|
-
if (typeof urlOrRequest === 'string') {
|
|
5851
|
-
url = urlOrRequest;
|
|
5852
|
-
}
|
|
5853
|
-
else if (urlOrRequest instanceof Request) {
|
|
5854
|
-
url = urlOrRequest.url;
|
|
5855
|
-
}
|
|
5856
|
-
throw new PromptbookFetchError(spaceTrim__default["default"]((block) => `
|
|
5857
|
-
Can not fetch "${url}"
|
|
5858
|
-
|
|
5859
|
-
Fetch error:
|
|
5860
|
-
${block(error.message)}
|
|
5861
|
-
|
|
5862
|
-
`));
|
|
5863
|
-
}
|
|
5864
|
-
};
|
|
5865
|
-
/**
|
|
5866
|
-
* TODO: [🧠] Maybe rename because it is not used only for scrapers but also in `$getCompiledBook`
|
|
5867
|
-
*/
|
|
5868
|
-
|
|
5869
7324
|
/**
|
|
5870
7325
|
* Factory function that creates a handler for processing knowledge sources.
|
|
5871
7326
|
* Provides standardized processing of different types of knowledge sources
|
|
@@ -5920,7 +7375,23 @@
|
|
|
5920
7375
|
// <- TODO: [🥬] Encapsulate sha256 to some private utility function
|
|
5921
7376
|
const rootDirname = path.join(process.cwd(), DEFAULT_DOWNLOAD_CACHE_DIRNAME);
|
|
5922
7377
|
const filepath = path.join(...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${basename.substring(0, MAX_FILENAME_LENGTH)}.${mimeTypeToExtension(mimeType)}`);
|
|
5923
|
-
|
|
7378
|
+
// Note: Try to create cache directory, but don't fail if filesystem has issues
|
|
7379
|
+
try {
|
|
7380
|
+
await tools.fs.mkdir(path.dirname(path.join(rootDirname, filepath)), { recursive: true });
|
|
7381
|
+
}
|
|
7382
|
+
catch (error) {
|
|
7383
|
+
// Note: If we can't create cache directory, we'll handle it when trying to write the file
|
|
7384
|
+
// This handles read-only filesystems, permission issues, and missing parent directories
|
|
7385
|
+
if (error instanceof Error && (error.message.includes('EROFS') ||
|
|
7386
|
+
error.message.includes('read-only') ||
|
|
7387
|
+
error.message.includes('EACCES') ||
|
|
7388
|
+
error.message.includes('EPERM') ||
|
|
7389
|
+
error.message.includes('ENOENT'))) ;
|
|
7390
|
+
else {
|
|
7391
|
+
// Re-throw other unexpected errors
|
|
7392
|
+
throw error;
|
|
7393
|
+
}
|
|
7394
|
+
}
|
|
5924
7395
|
const fileContent = Buffer.from(await response.arrayBuffer());
|
|
5925
7396
|
if (fileContent.length > DEFAULT_MAX_FILE_SIZE /* <- TODO: Allow to pass different value to remote server */) {
|
|
5926
7397
|
throw new LimitReachedError(`File is too large (${Math.round(fileContent.length / 1024 / 1024)}MB). Maximum allowed size is ${Math.round(DEFAULT_MAX_FILE_SIZE / 1024 / 1024)}MB.`);
|
|
@@ -5935,7 +7406,8 @@
|
|
|
5935
7406
|
if (error instanceof Error && (error.message.includes('EROFS') ||
|
|
5936
7407
|
error.message.includes('read-only') ||
|
|
5937
7408
|
error.message.includes('EACCES') ||
|
|
5938
|
-
error.message.includes('EPERM')
|
|
7409
|
+
error.message.includes('EPERM') ||
|
|
7410
|
+
error.message.includes('ENOENT'))) {
|
|
5939
7411
|
// Return a handler that works directly with the downloaded content
|
|
5940
7412
|
return {
|
|
5941
7413
|
source: name,
|
|
@@ -11632,18 +13104,28 @@
|
|
|
11632
13104
|
* @public exported from `@promptbook/core`
|
|
11633
13105
|
*/
|
|
11634
13106
|
function book(strings, ...values) {
|
|
11635
|
-
const
|
|
11636
|
-
if (!isValidPipelineString(
|
|
13107
|
+
const bookString = prompt(strings, ...values);
|
|
13108
|
+
if (!isValidPipelineString(bookString)) {
|
|
11637
13109
|
// TODO: Make the CustomError for this
|
|
11638
13110
|
throw new Error(spaceTrim__default["default"](`
|
|
11639
13111
|
The string is not a valid pipeline string
|
|
11640
13112
|
|
|
11641
13113
|
book\`
|
|
11642
|
-
${
|
|
13114
|
+
${bookString}
|
|
11643
13115
|
\`
|
|
11644
13116
|
`));
|
|
11645
13117
|
}
|
|
11646
|
-
|
|
13118
|
+
if (!isValidBook(bookString)) {
|
|
13119
|
+
// TODO: Make the CustomError for this
|
|
13120
|
+
throw new Error(spaceTrim__default["default"](`
|
|
13121
|
+
The string is not a valid book
|
|
13122
|
+
|
|
13123
|
+
book\`
|
|
13124
|
+
${bookString}
|
|
13125
|
+
\`
|
|
13126
|
+
`));
|
|
13127
|
+
}
|
|
13128
|
+
return bookString;
|
|
11647
13129
|
}
|
|
11648
13130
|
/**
|
|
11649
13131
|
* TODO: [🧠][🈴] Where is the best location for this file
|
|
@@ -11994,6 +13476,7 @@
|
|
|
11994
13476
|
exports.CompletionFormfactorDefinition = CompletionFormfactorDefinition;
|
|
11995
13477
|
exports.CsvFormatError = CsvFormatError;
|
|
11996
13478
|
exports.CsvFormatParser = CsvFormatParser;
|
|
13479
|
+
exports.DEFAULT_BOOK = DEFAULT_BOOK;
|
|
11997
13480
|
exports.DEFAULT_BOOKS_DIRNAME = DEFAULT_BOOKS_DIRNAME;
|
|
11998
13481
|
exports.DEFAULT_BOOK_OUTPUT_PARAMETER_NAME = DEFAULT_BOOK_OUTPUT_PARAMETER_NAME;
|
|
11999
13482
|
exports.DEFAULT_BOOK_TITLE = DEFAULT_BOOK_TITLE;
|
|
@@ -12038,6 +13521,7 @@
|
|
|
12038
13521
|
exports.NAME = NAME;
|
|
12039
13522
|
exports.NonTaskSectionTypes = NonTaskSectionTypes;
|
|
12040
13523
|
exports.NotFoundError = NotFoundError;
|
|
13524
|
+
exports.NotYetImplementedCommitmentDefinition = NotYetImplementedCommitmentDefinition;
|
|
12041
13525
|
exports.NotYetImplementedError = NotYetImplementedError;
|
|
12042
13526
|
exports.ORDER_OF_PIPELINE_JSON = ORDER_OF_PIPELINE_JSON;
|
|
12043
13527
|
exports.PENDING_VALUE_PLACEHOLDER = PENDING_VALUE_PLACEHOLDER;
|
|
@@ -12086,9 +13570,12 @@
|
|
|
12086
13570
|
exports.compilePipeline = compilePipeline;
|
|
12087
13571
|
exports.computeCosineSimilarity = computeCosineSimilarity;
|
|
12088
13572
|
exports.countUsage = countUsage;
|
|
13573
|
+
exports.createAgentModelRequirements = createAgentModelRequirements;
|
|
13574
|
+
exports.createBasicAgentModelRequirements = createBasicAgentModelRequirements;
|
|
12089
13575
|
exports.createCollectionFromJson = createCollectionFromJson;
|
|
12090
13576
|
exports.createCollectionFromPromise = createCollectionFromPromise;
|
|
12091
13577
|
exports.createCollectionFromUrl = createCollectionFromUrl;
|
|
13578
|
+
exports.createEmptyAgentModelRequirements = createEmptyAgentModelRequirements;
|
|
12092
13579
|
exports.createLlmToolsFromConfiguration = createLlmToolsFromConfiguration;
|
|
12093
13580
|
exports.createPipelineExecutor = createPipelineExecutor;
|
|
12094
13581
|
exports.createSubcollection = createSubcollection;
|
|
@@ -12096,17 +13583,23 @@
|
|
|
12096
13583
|
exports.executionReportJsonToString = executionReportJsonToString;
|
|
12097
13584
|
exports.extractParameterNamesFromTask = extractParameterNamesFromTask;
|
|
12098
13585
|
exports.filterModels = filterModels;
|
|
13586
|
+
exports.getAllCommitmentDefinitions = getAllCommitmentDefinitions;
|
|
13587
|
+
exports.getAllCommitmentTypes = getAllCommitmentTypes;
|
|
13588
|
+
exports.getCommitmentDefinition = getCommitmentDefinition;
|
|
12099
13589
|
exports.getPipelineInterface = getPipelineInterface;
|
|
12100
13590
|
exports.identificationToPromptbookToken = identificationToPromptbookToken;
|
|
13591
|
+
exports.isCommitmentSupported = isCommitmentSupported;
|
|
12101
13592
|
exports.isPassingExpectations = isPassingExpectations;
|
|
12102
13593
|
exports.isPipelineImplementingInterface = isPipelineImplementingInterface;
|
|
12103
13594
|
exports.isPipelineInterfacesEqual = isPipelineInterfacesEqual;
|
|
12104
13595
|
exports.isPipelinePrepared = isPipelinePrepared;
|
|
13596
|
+
exports.isValidBook = isValidBook;
|
|
12105
13597
|
exports.isValidPipelineString = isValidPipelineString;
|
|
12106
13598
|
exports.joinLlmExecutionTools = joinLlmExecutionTools;
|
|
12107
13599
|
exports.limitTotalUsage = limitTotalUsage;
|
|
12108
13600
|
exports.makeKnowledgeSourceHandler = makeKnowledgeSourceHandler;
|
|
12109
13601
|
exports.migratePipeline = migratePipeline;
|
|
13602
|
+
exports.parseAgentSource = parseAgentSource;
|
|
12110
13603
|
exports.parsePipeline = parsePipeline;
|
|
12111
13604
|
exports.pipelineJsonToString = pipelineJsonToString;
|
|
12112
13605
|
exports.prepareKnowledgePieces = prepareKnowledgePieces;
|
|
@@ -12118,6 +13611,7 @@
|
|
|
12118
13611
|
exports.unpreparePipeline = unpreparePipeline;
|
|
12119
13612
|
exports.usageToHuman = usageToHuman;
|
|
12120
13613
|
exports.usageToWorktime = usageToWorktime;
|
|
13614
|
+
exports.validateBook = validateBook;
|
|
12121
13615
|
exports.validatePipeline = validatePipeline;
|
|
12122
13616
|
exports.validatePipelineString = validatePipelineString;
|
|
12123
13617
|
|