@promptbook/core 0.103.0-48 → 0.103.0-50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.es.js +578 -331
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +1 -0
- package/esm/typings/src/_packages/components.index.d.ts +2 -0
- package/esm/typings/src/_packages/types.index.d.ts +2 -0
- package/esm/typings/src/_packages/utils.index.d.ts +2 -0
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +12 -2
- package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgent.d.ts +20 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +14 -8
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabaseOptions.d.ts +10 -0
- package/esm/typings/src/commitments/MESSAGE/InitialMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/config.d.ts +1 -0
- package/esm/typings/src/errors/DatabaseError.d.ts +2 -2
- package/esm/typings/src/errors/WrappedError.d.ts +2 -2
- package/esm/typings/src/execution/ExecutionTask.d.ts +2 -2
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +6 -1
- package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizardOrCli.d.ts +2 -2
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +19 -3
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +13 -1
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +11 -2
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +6 -1
- package/esm/typings/src/remote-server/startAgentServer.d.ts +2 -2
- package/esm/typings/src/utils/color/Color.d.ts +7 -0
- package/esm/typings/src/utils/color/Color.test.d.ts +1 -0
- package/esm/typings/src/utils/environment/$getGlobalScope.d.ts +2 -2
- package/esm/typings/src/utils/misc/computeHash.d.ts +11 -0
- package/esm/typings/src/utils/misc/computeHash.test.d.ts +1 -0
- package/esm/typings/src/utils/organization/$sideEffect.d.ts +2 -2
- package/esm/typings/src/utils/organization/$side_effect.d.ts +2 -2
- package/esm/typings/src/utils/organization/TODO_USE.d.ts +2 -2
- package/esm/typings/src/utils/organization/keepUnused.d.ts +2 -2
- package/esm/typings/src/utils/organization/preserve.d.ts +3 -3
- package/esm/typings/src/utils/organization/really_any.d.ts +7 -0
- package/esm/typings/src/utils/serialization/asSerializable.d.ts +2 -2
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +558 -311
- package/umd/index.umd.js.map +1 -1
package/esm/index.es.js
CHANGED
|
@@ -27,140 +27,12 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
27
27
|
* @generated
|
|
28
28
|
* @see https://github.com/webgptorg/promptbook
|
|
29
29
|
*/
|
|
30
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.103.0-
|
|
30
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.103.0-50';
|
|
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
|
-
/**
|
|
37
|
-
* Computes SHA-256 hash of the agent source
|
|
38
|
-
*
|
|
39
|
-
* @public exported from `@promptbook/core`
|
|
40
|
-
*/
|
|
41
|
-
function computeAgentHash(agentSource) {
|
|
42
|
-
return SHA256(hexEncoder.parse(agentSource /* <- TODO: !!!!! spaceTrim */)).toString( /* hex */);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n```json\n{availableModels}\n```\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelsRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Checks if value is valid email
|
|
49
|
-
*
|
|
50
|
-
* @public exported from `@promptbook/utils`
|
|
51
|
-
*/
|
|
52
|
-
function isValidEmail(email) {
|
|
53
|
-
if (typeof email !== 'string') {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
if (email.split('\n').length > 1) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
return /^.+@.+\..+$/.test(email);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Tests if given string is valid file path.
|
|
64
|
-
*
|
|
65
|
-
* Note: This does not check if the file exists only if the path is valid
|
|
66
|
-
* @public exported from `@promptbook/utils`
|
|
67
|
-
*/
|
|
68
|
-
function isValidFilePath(filename) {
|
|
69
|
-
if (typeof filename !== 'string') {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
if (filename.split('\n').length > 1) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
// Normalize slashes early so heuristics can detect path-like inputs
|
|
76
|
-
const filenameSlashes = filename.replace(/\\/g, '/');
|
|
77
|
-
// Reject strings that look like sentences (informational text)
|
|
78
|
-
// Heuristic: contains multiple spaces and ends with a period, or contains typical sentence punctuation
|
|
79
|
-
// But skip this heuristic if the string looks like a path (contains '/' or starts with a drive letter)
|
|
80
|
-
if (filename.trim().length > 60 && // long enough to be a sentence
|
|
81
|
-
/[.!?]/.test(filename) && // contains sentence punctuation
|
|
82
|
-
filename.split(' ').length > 8 && // has many words
|
|
83
|
-
!/\/|^[A-Z]:/i.test(filenameSlashes) // do NOT treat as sentence if looks like a path
|
|
84
|
-
) {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
// Absolute Unix path: /hello.txt
|
|
88
|
-
if (/^(\/)/i.test(filenameSlashes)) {
|
|
89
|
-
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
// Absolute Windows path: C:/ or C:\ (allow spaces and multiple dots in filename)
|
|
93
|
-
if (/^[A-Z]:\/.+$/i.test(filenameSlashes)) {
|
|
94
|
-
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
// Relative path: ./hello.txt
|
|
98
|
-
if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
|
|
99
|
-
// console.log(filename, 'Relative path: ./hello.txt');
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
// Allow paths like foo/hello
|
|
103
|
-
if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
|
|
104
|
-
// console.log(filename, 'Allow paths like foo/hello');
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
// Allow paths like hello.book
|
|
108
|
-
if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
|
|
109
|
-
// console.log(filename, 'Allow paths like hello.book');
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* TODO: [🍏] Implement for MacOs
|
|
116
|
-
*/
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Tests if given string is valid URL.
|
|
120
|
-
*
|
|
121
|
-
* Note: [🔂] This function is idempotent.
|
|
122
|
-
* Note: Dataurl are considered perfectly valid.
|
|
123
|
-
* Note: There are two similar functions:
|
|
124
|
-
* - `isValidUrl` which tests any URL
|
|
125
|
-
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
126
|
-
*
|
|
127
|
-
* @public exported from `@promptbook/utils`
|
|
128
|
-
*/
|
|
129
|
-
function isValidUrl(url) {
|
|
130
|
-
if (typeof url !== 'string') {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
try {
|
|
134
|
-
if (url.startsWith('blob:')) {
|
|
135
|
-
url = url.replace(/^blob:/, '');
|
|
136
|
-
}
|
|
137
|
-
const urlObject = new URL(url /* because fail is handled */);
|
|
138
|
-
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
150
|
-
*
|
|
151
|
-
* @public exported from `@promptbook/core`
|
|
152
|
-
*/
|
|
153
|
-
class ParseError extends Error {
|
|
154
|
-
constructor(message) {
|
|
155
|
-
super(message);
|
|
156
|
-
this.name = 'ParseError';
|
|
157
|
-
Object.setPrototypeOf(this, ParseError.prototype);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
162
|
-
*/
|
|
163
|
-
|
|
164
36
|
/**
|
|
165
37
|
* Available remote servers for the Promptbook
|
|
166
38
|
*
|
|
@@ -194,6 +66,7 @@ const REMOTE_SERVER_URLS = [
|
|
|
194
66
|
*/
|
|
195
67
|
];
|
|
196
68
|
/**
|
|
69
|
+
* TODO: [🐱🚀] Auto-federated server from url in here
|
|
197
70
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
198
71
|
*/
|
|
199
72
|
|
|
@@ -527,6 +400,9 @@ class Color {
|
|
|
527
400
|
if (hex.length === 3) {
|
|
528
401
|
return Color.fromHex3(hex);
|
|
529
402
|
}
|
|
403
|
+
if (hex.length === 4) {
|
|
404
|
+
return Color.fromHex4(hex);
|
|
405
|
+
}
|
|
530
406
|
if (hex.length === 6) {
|
|
531
407
|
return Color.fromHex6(hex);
|
|
532
408
|
}
|
|
@@ -547,6 +423,19 @@ class Color {
|
|
|
547
423
|
const b = parseInt(hex.substr(2, 1), 16) * 16;
|
|
548
424
|
return take(new Color(r, g, b));
|
|
549
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Creates a new Color instance from color in hex format with 4 digits (with alpha channel)
|
|
428
|
+
*
|
|
429
|
+
* @param color in hex for example `09df`
|
|
430
|
+
* @returns Color object
|
|
431
|
+
*/
|
|
432
|
+
static fromHex4(hex) {
|
|
433
|
+
const r = parseInt(hex.substr(0, 1), 16) * 16;
|
|
434
|
+
const g = parseInt(hex.substr(1, 1), 16) * 16;
|
|
435
|
+
const b = parseInt(hex.substr(2, 1), 16) * 16;
|
|
436
|
+
const a = parseInt(hex.substr(3, 1), 16) * 16;
|
|
437
|
+
return take(new Color(r, g, b, a));
|
|
438
|
+
}
|
|
550
439
|
/**
|
|
551
440
|
* Creates a new Color instance from color in hex format with 6 color digits (without alpha channel)
|
|
552
441
|
*
|
|
@@ -737,7 +626,8 @@ class Color {
|
|
|
737
626
|
* @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
|
|
738
627
|
*/
|
|
739
628
|
static isHexColorString(value) {
|
|
740
|
-
return typeof value === 'string' &&
|
|
629
|
+
return (typeof value === 'string' &&
|
|
630
|
+
/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
|
|
741
631
|
}
|
|
742
632
|
/**
|
|
743
633
|
* Creates new Color object
|
|
@@ -1078,6 +968,7 @@ const PROMPTBOOK_COLOR = Color.fromHex('#79EAFD');
|
|
|
1078
968
|
const PROMPTBOOK_SYNTAX_COLORS = {
|
|
1079
969
|
TITLE: Color.fromHex('#244EA8'),
|
|
1080
970
|
LINE: Color.fromHex('#eeeeee'),
|
|
971
|
+
SEPARATOR: Color.fromHex('#cccccc'),
|
|
1081
972
|
COMMITMENT: Color.fromHex('#DA0F78'),
|
|
1082
973
|
PARAMETER: Color.fromHex('#8e44ad'),
|
|
1083
974
|
};
|
|
@@ -1480,52 +1371,273 @@ class UnexpectedError extends Error {
|
|
|
1480
1371
|
}
|
|
1481
1372
|
|
|
1482
1373
|
/**
|
|
1483
|
-
* This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
|
|
1374
|
+
* This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
|
|
1375
|
+
*
|
|
1376
|
+
* @public exported from `@promptbook/core`
|
|
1377
|
+
*/
|
|
1378
|
+
class WrappedError extends Error {
|
|
1379
|
+
constructor(whatWasThrown) {
|
|
1380
|
+
const tag = `[🤮]`;
|
|
1381
|
+
console.error(tag, whatWasThrown);
|
|
1382
|
+
super(spaceTrim$2(`
|
|
1383
|
+
Non-Error object was thrown
|
|
1384
|
+
|
|
1385
|
+
Note: Look for ${tag} in the console for more details
|
|
1386
|
+
Please report issue on ${ADMIN_EMAIL}
|
|
1387
|
+
`));
|
|
1388
|
+
this.name = 'WrappedError';
|
|
1389
|
+
Object.setPrototypeOf(this, WrappedError.prototype);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/**
|
|
1394
|
+
* Helper used in catch blocks to assert that the error is an instance of `Error`
|
|
1395
|
+
*
|
|
1396
|
+
* @param whatWasThrown Any object that was thrown
|
|
1397
|
+
* @returns Nothing if the error is an instance of `Error`
|
|
1398
|
+
* @throws `WrappedError` or `UnexpectedError` if the error is not standard
|
|
1399
|
+
*
|
|
1400
|
+
* @private within the repository
|
|
1401
|
+
*/
|
|
1402
|
+
function assertsError(whatWasThrown) {
|
|
1403
|
+
// Case 1: Handle error which was rethrown as `WrappedError`
|
|
1404
|
+
if (whatWasThrown instanceof WrappedError) {
|
|
1405
|
+
const wrappedError = whatWasThrown;
|
|
1406
|
+
throw wrappedError;
|
|
1407
|
+
}
|
|
1408
|
+
// Case 2: Handle unexpected errors
|
|
1409
|
+
if (whatWasThrown instanceof UnexpectedError) {
|
|
1410
|
+
const unexpectedError = whatWasThrown;
|
|
1411
|
+
throw unexpectedError;
|
|
1412
|
+
}
|
|
1413
|
+
// Case 3: Handle standard errors - keep them up to consumer
|
|
1414
|
+
if (whatWasThrown instanceof Error) {
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
// Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
|
|
1418
|
+
throw new WrappedError(whatWasThrown);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Format either small or big number
|
|
1423
|
+
*
|
|
1424
|
+
* @public exported from `@promptbook/utils`
|
|
1425
|
+
*/
|
|
1426
|
+
function numberToString(value) {
|
|
1427
|
+
if (value === 0) {
|
|
1428
|
+
return '0';
|
|
1429
|
+
}
|
|
1430
|
+
else if (Number.isNaN(value)) {
|
|
1431
|
+
return VALUE_STRINGS.nan;
|
|
1432
|
+
}
|
|
1433
|
+
else if (value === Infinity) {
|
|
1434
|
+
return VALUE_STRINGS.infinity;
|
|
1435
|
+
}
|
|
1436
|
+
else if (value === -Infinity) {
|
|
1437
|
+
return VALUE_STRINGS.negativeInfinity;
|
|
1438
|
+
}
|
|
1439
|
+
for (let exponent = 0; exponent < 15; exponent++) {
|
|
1440
|
+
const factor = 10 ** exponent;
|
|
1441
|
+
const valueRounded = Math.round(value * factor) / factor;
|
|
1442
|
+
if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
|
|
1443
|
+
return valueRounded.toFixed(exponent);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return value.toString();
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/**
|
|
1450
|
+
* Function `valueToString` will convert the given value to string
|
|
1451
|
+
* This is useful and used in the `templateParameters` function
|
|
1452
|
+
*
|
|
1453
|
+
* Note: This function is not just calling `toString` method
|
|
1454
|
+
* It's more complex and can handle this conversion specifically for LLM models
|
|
1455
|
+
* See `VALUE_STRINGS`
|
|
1456
|
+
*
|
|
1457
|
+
* Note: There are 2 similar functions
|
|
1458
|
+
* - `valueToString` converts value to string for LLM models as human-readable string
|
|
1459
|
+
* - `asSerializable` converts value to string to preserve full information to be able to convert it back
|
|
1460
|
+
*
|
|
1461
|
+
* @public exported from `@promptbook/utils`
|
|
1462
|
+
*/
|
|
1463
|
+
function valueToString(value) {
|
|
1464
|
+
try {
|
|
1465
|
+
if (value === '') {
|
|
1466
|
+
return VALUE_STRINGS.empty;
|
|
1467
|
+
}
|
|
1468
|
+
else if (value === null) {
|
|
1469
|
+
return VALUE_STRINGS.null;
|
|
1470
|
+
}
|
|
1471
|
+
else if (value === undefined) {
|
|
1472
|
+
return VALUE_STRINGS.undefined;
|
|
1473
|
+
}
|
|
1474
|
+
else if (typeof value === 'string') {
|
|
1475
|
+
return value;
|
|
1476
|
+
}
|
|
1477
|
+
else if (typeof value === 'number') {
|
|
1478
|
+
return numberToString(value);
|
|
1479
|
+
}
|
|
1480
|
+
else if (value instanceof Date) {
|
|
1481
|
+
return value.toISOString();
|
|
1482
|
+
}
|
|
1483
|
+
else {
|
|
1484
|
+
try {
|
|
1485
|
+
return JSON.stringify(value);
|
|
1486
|
+
}
|
|
1487
|
+
catch (error) {
|
|
1488
|
+
if (error instanceof TypeError && error.message.includes('circular structure')) {
|
|
1489
|
+
return VALUE_STRINGS.circular;
|
|
1490
|
+
}
|
|
1491
|
+
throw error;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
catch (error) {
|
|
1496
|
+
assertsError(error);
|
|
1497
|
+
console.error(error);
|
|
1498
|
+
return VALUE_STRINGS.unserializable;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
/**
|
|
1503
|
+
* Computes SHA-256 hash of the given object
|
|
1504
|
+
*
|
|
1505
|
+
* @public exported from `@promptbook/utils`
|
|
1506
|
+
*/
|
|
1507
|
+
function computeHash(value) {
|
|
1508
|
+
return SHA256(hexEncoder.parse(spaceTrim$1(valueToString(value)))).toString( /* hex */);
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* TODO: [🥬][🥬] Use this ACRY
|
|
1512
|
+
*/
|
|
1513
|
+
|
|
1514
|
+
/**
|
|
1515
|
+
* Computes SHA-256 hash of the agent source
|
|
1516
|
+
*
|
|
1517
|
+
* @public exported from `@promptbook/core`
|
|
1518
|
+
*/
|
|
1519
|
+
function computeAgentHash(agentSource) {
|
|
1520
|
+
return computeHash(agentSource);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
var PipelineCollection = [{title:"Prepare Knowledge from Markdown",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book",formfactorName:"GENERIC",parameters:[{name:"knowledgeContent",description:"Markdown document content",isInput:true,isOutput:false},{name:"knowledgePieces",description:"The knowledge JSON object",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}",resultingParameterName:"knowledgePieces",dependentParameterNames:["knowledgeContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge from Markdown\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-from-markdown.book`\n- INPUT PARAMETER `{knowledgeContent}` Markdown document content\n- OUTPUT PARAMETER `{knowledgePieces}` The knowledge JSON object\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, extract the important knowledge from the document.\n\n# Rules\n\n- Make pieces of information concise, clear, and easy to understand\n- One piece of information should be approximately 1 paragraph\n- Divide the paragraphs by markdown horizontal lines ---\n- Omit irrelevant information\n- Group redundant information\n- Write just extracted information, nothing else\n\n# The document\n\nTake information from this document:\n\n> {knowledgeContent}\n```\n\n`-> {knowledgePieces}`\n"}],sourceFile:"./books/prepare-knowledge-from-markdown.book"},{title:"Prepare Keywords",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-keywords.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"keywords",description:"Keywords separated by comma",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}",resultingParameterName:"keywords",dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Keywords\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-keywords.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{keywords}` Keywords separated by comma\n\n## Knowledge\n\n<!-- TODO: [🍆] -FORMAT JSON -->\n\n```markdown\nYou are experienced data researcher, detect the important keywords in the document.\n\n# Rules\n\n- Write just keywords separated by comma\n\n# The document\n\nTake information from this document:\n\n> {knowledgePieceContent}\n```\n\n`-> {keywords}`\n"}],sourceFile:"./books/prepare-knowledge-keywords.book"},{title:"Prepare Knowledge-piece Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-knowledge-title.book",formfactorName:"GENERIC",parameters:[{name:"knowledgePieceContent",description:"The content",isInput:true,isOutput:false},{name:"title",description:"The title of the document",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"knowledge",title:"Knowledge",content:"You are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}",resultingParameterName:"title",expectations:{words:{min:1,max:8}},dependentParameterNames:["knowledgePieceContent"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Knowledge-piece Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-knowledge-title.book`\n- INPUT PARAMETER `{knowledgePieceContent}` The content\n- OUTPUT PARAMETER `{title}` The title of the document\n\n## Knowledge\n\n- EXPECT MIN 1 WORD\n- EXPECT MAX 8 WORDS\n\n```markdown\nYou are experienced content creator, write best title for the document.\n\n# Rules\n\n- Write just title, nothing else\n- Write maximum 5 words for the title\n\n# The document\n\n> {knowledgePieceContent}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-knowledge-title.book"},{title:"Prepare Persona",pipelineUrl:"https://promptbook.studio/promptbook/prepare-persona.book",formfactorName:"GENERIC",parameters:[{name:"availableModels",description:"List of available model names together with their descriptions as JSON",isInput:true,isOutput:false},{name:"personaDescription",description:"Description of the persona",isInput:true,isOutput:false},{name:"modelsRequirements",description:"Specific requirements for the model",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-model-requirements",title:"Make modelRequirements",content:"You are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n```json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n```\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n```json\n{availableModels}\n```\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}",resultingParameterName:"modelsRequirements",format:"JSON",dependentParameterNames:["availableModels","personaDescription"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Persona\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-persona.book`\n- INPUT PARAMETER `{availableModels}` List of available model names together with their descriptions as JSON\n- INPUT PARAMETER `{personaDescription}` Description of the persona\n- OUTPUT PARAMETER `{modelsRequirements}` Specific requirements for the model\n\n## Make modelRequirements\n\n- FORMAT JSON\n\n```markdown\nYou are an experienced AI engineer, you need to find the best models for virtual assistants:\n\n## Example\n\n\\`\\`\\`json\n[\n {\n \"modelName\": \"gpt-4o\",\n \"systemMessage\": \"You are experienced AI engineer and helpful assistant.\",\n \"temperature\": 0.7\n },\n {\n \"modelName\": \"claude-3-5-sonnet\",\n \"systemMessage\": \"You are a friendly and knowledgeable chatbot.\",\n \"temperature\": 0.5\n }\n]\n\\`\\`\\`\n\n## Instructions\n\n- Your output format is JSON array\n- Sort best-fitting models first\n- Omit any models that are not suitable\n- Write just the JSON, no other text should be present\n- Array contain items with following keys:\n - `modelName`: The name of the model to use\n - `systemMessage`: The system message to provide context to the model\n - `temperature`: The sampling temperature to use\n\n### Key `modelName`\n\nHere are the available models:\n\n\\`\\`\\`json\n{availableModels}\n\\`\\`\\`\n\n### Key `systemMessage`\n\nThe system message is used to communicate instructions or provide context to the model at the beginning of a conversation. It is displayed in a different format compared to user messages, helping the model understand its role in the conversation. The system message typically guides the model's behavior, sets the tone, or specifies desired output from the model. By utilizing the system message effectively, users can steer the model towards generating more accurate and relevant responses.\n\nFor example:\n\n> You are an experienced AI engineer and helpful assistant.\n\n> You are a friendly and knowledgeable chatbot.\n\n### Key `temperature`\n\nThe sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.\n\nYou can pick a value between 0 and 2. For example:\n\n- `0.1`: Low temperature, extremely conservative and deterministic\n- `0.5`: Medium temperature, balanced between conservative and creative\n- `1.0`: High temperature, creative and bit random\n- `1.5`: Very high temperature, extremely creative and often chaotic and unpredictable\n- `2.0`: Maximum temperature, completely random and unpredictable, for some extreme creative use cases\n\n# The assistant\n\nTake this description of the persona:\n\n> {personaDescription}\n```\n\n`-> {modelsRequirements}`\n"}],sourceFile:"./books/prepare-persona.book"},{title:"Prepare Title",pipelineUrl:"https://promptbook.studio/promptbook/prepare-title.book",formfactorName:"GENERIC",parameters:[{name:"book",description:"The book to prepare the title for",isInput:true,isOutput:false},{name:"title",description:"Best title for the book",isInput:false,isOutput:true}],tasks:[{taskType:"PROMPT_TASK",name:"make-title",title:"Make title",content:"Make best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}",resultingParameterName:"title",expectations:{words:{min:1,max:8},lines:{min:1,max:1}},dependentParameterNames:["book"]}],personas:[],preparations:[],knowledgeSources:[],knowledgePieces:[],sources:[{type:"BOOK",path:null,content:"# Prepare Title\n\n- PIPELINE URL `https://promptbook.studio/promptbook/prepare-title.book`\n- INPUT PARAMETER `{book}` The book to prepare the title for\n- OUTPUT PARAMETER `{title}` Best title for the book\n\n## Make title\n\n- EXPECT MIN 1 Word\n- EXPECT MAX 8 Words\n- EXPECT EXACTLY 1 Line\n\n```markdown\nMake best title for given text which describes the workflow:\n\n## Rules\n\n- Write just title, nothing else\n- Title should be concise and clear - Write maximum ideally 2 words, maximum 5 words\n- Title starts with emoticon\n- Title should not mention the input and output of the workflow but the main purpose of the workflow\n _For example, not \"✍ Convert Knowledge-piece to title\" but \"✍ Title\"_\n\n## The workflow\n\n> {book}\n```\n\n`-> {title}`\n"}],sourceFile:"./books/prepare-title.book"}];
|
|
1524
|
+
|
|
1525
|
+
/**
|
|
1526
|
+
* Checks if value is valid email
|
|
1527
|
+
*
|
|
1528
|
+
* @public exported from `@promptbook/utils`
|
|
1529
|
+
*/
|
|
1530
|
+
function isValidEmail(email) {
|
|
1531
|
+
if (typeof email !== 'string') {
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
if (email.split('\n').length > 1) {
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
return /^.+@.+\..+$/.test(email);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* Tests if given string is valid file path.
|
|
1542
|
+
*
|
|
1543
|
+
* Note: This does not check if the file exists only if the path is valid
|
|
1544
|
+
* @public exported from `@promptbook/utils`
|
|
1545
|
+
*/
|
|
1546
|
+
function isValidFilePath(filename) {
|
|
1547
|
+
if (typeof filename !== 'string') {
|
|
1548
|
+
return false;
|
|
1549
|
+
}
|
|
1550
|
+
if (filename.split('\n').length > 1) {
|
|
1551
|
+
return false;
|
|
1552
|
+
}
|
|
1553
|
+
// Normalize slashes early so heuristics can detect path-like inputs
|
|
1554
|
+
const filenameSlashes = filename.replace(/\\/g, '/');
|
|
1555
|
+
// Reject strings that look like sentences (informational text)
|
|
1556
|
+
// Heuristic: contains multiple spaces and ends with a period, or contains typical sentence punctuation
|
|
1557
|
+
// But skip this heuristic if the string looks like a path (contains '/' or starts with a drive letter)
|
|
1558
|
+
if (filename.trim().length > 60 && // long enough to be a sentence
|
|
1559
|
+
/[.!?]/.test(filename) && // contains sentence punctuation
|
|
1560
|
+
filename.split(' ').length > 8 && // has many words
|
|
1561
|
+
!/\/|^[A-Z]:/i.test(filenameSlashes) // do NOT treat as sentence if looks like a path
|
|
1562
|
+
) {
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1565
|
+
// Absolute Unix path: /hello.txt
|
|
1566
|
+
if (/^(\/)/i.test(filenameSlashes)) {
|
|
1567
|
+
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
1568
|
+
return true;
|
|
1569
|
+
}
|
|
1570
|
+
// Absolute Windows path: C:/ or C:\ (allow spaces and multiple dots in filename)
|
|
1571
|
+
if (/^[A-Z]:\/.+$/i.test(filenameSlashes)) {
|
|
1572
|
+
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
1573
|
+
return true;
|
|
1574
|
+
}
|
|
1575
|
+
// Relative path: ./hello.txt
|
|
1576
|
+
if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
|
|
1577
|
+
// console.log(filename, 'Relative path: ./hello.txt');
|
|
1578
|
+
return true;
|
|
1579
|
+
}
|
|
1580
|
+
// Allow paths like foo/hello
|
|
1581
|
+
if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
|
|
1582
|
+
// console.log(filename, 'Allow paths like foo/hello');
|
|
1583
|
+
return true;
|
|
1584
|
+
}
|
|
1585
|
+
// Allow paths like hello.book
|
|
1586
|
+
if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
|
|
1587
|
+
// console.log(filename, 'Allow paths like hello.book');
|
|
1588
|
+
return true;
|
|
1589
|
+
}
|
|
1590
|
+
return false;
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* TODO: [🍏] Implement for MacOs
|
|
1594
|
+
*/
|
|
1595
|
+
|
|
1596
|
+
/**
|
|
1597
|
+
* Tests if given string is valid URL.
|
|
1598
|
+
*
|
|
1599
|
+
* Note: [🔂] This function is idempotent.
|
|
1600
|
+
* Note: Dataurl are considered perfectly valid.
|
|
1601
|
+
* Note: There are two similar functions:
|
|
1602
|
+
* - `isValidUrl` which tests any URL
|
|
1603
|
+
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
1604
|
+
*
|
|
1605
|
+
* @public exported from `@promptbook/utils`
|
|
1606
|
+
*/
|
|
1607
|
+
function isValidUrl(url) {
|
|
1608
|
+
if (typeof url !== 'string') {
|
|
1609
|
+
return false;
|
|
1610
|
+
}
|
|
1611
|
+
try {
|
|
1612
|
+
if (url.startsWith('blob:')) {
|
|
1613
|
+
url = url.replace(/^blob:/, '');
|
|
1614
|
+
}
|
|
1615
|
+
const urlObject = new URL(url /* because fail is handled */);
|
|
1616
|
+
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
1617
|
+
return false;
|
|
1618
|
+
}
|
|
1619
|
+
return true;
|
|
1620
|
+
}
|
|
1621
|
+
catch (error) {
|
|
1622
|
+
return false;
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
1484
1628
|
*
|
|
1485
1629
|
* @public exported from `@promptbook/core`
|
|
1486
1630
|
*/
|
|
1487
|
-
class
|
|
1488
|
-
constructor(
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
Non-Error object was thrown
|
|
1493
|
-
|
|
1494
|
-
Note: Look for ${tag} in the console for more details
|
|
1495
|
-
Please report issue on ${ADMIN_EMAIL}
|
|
1496
|
-
`));
|
|
1497
|
-
this.name = 'WrappedError';
|
|
1498
|
-
Object.setPrototypeOf(this, WrappedError.prototype);
|
|
1631
|
+
class ParseError extends Error {
|
|
1632
|
+
constructor(message) {
|
|
1633
|
+
super(message);
|
|
1634
|
+
this.name = 'ParseError';
|
|
1635
|
+
Object.setPrototypeOf(this, ParseError.prototype);
|
|
1499
1636
|
}
|
|
1500
1637
|
}
|
|
1501
|
-
|
|
1502
1638
|
/**
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1505
|
-
* @param whatWasThrown Any object that was thrown
|
|
1506
|
-
* @returns Nothing if the error is an instance of `Error`
|
|
1507
|
-
* @throws `WrappedError` or `UnexpectedError` if the error is not standard
|
|
1508
|
-
*
|
|
1509
|
-
* @private within the repository
|
|
1639
|
+
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
1510
1640
|
*/
|
|
1511
|
-
function assertsError(whatWasThrown) {
|
|
1512
|
-
// Case 1: Handle error which was rethrown as `WrappedError`
|
|
1513
|
-
if (whatWasThrown instanceof WrappedError) {
|
|
1514
|
-
const wrappedError = whatWasThrown;
|
|
1515
|
-
throw wrappedError;
|
|
1516
|
-
}
|
|
1517
|
-
// Case 2: Handle unexpected errors
|
|
1518
|
-
if (whatWasThrown instanceof UnexpectedError) {
|
|
1519
|
-
const unexpectedError = whatWasThrown;
|
|
1520
|
-
throw unexpectedError;
|
|
1521
|
-
}
|
|
1522
|
-
// Case 3: Handle standard errors - keep them up to consumer
|
|
1523
|
-
if (whatWasThrown instanceof Error) {
|
|
1524
|
-
return;
|
|
1525
|
-
}
|
|
1526
|
-
// Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
|
|
1527
|
-
throw new WrappedError(whatWasThrown);
|
|
1528
|
-
}
|
|
1529
1641
|
|
|
1530
1642
|
/**
|
|
1531
1643
|
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
@@ -1939,7 +2051,7 @@ function deepClone(objectValue) {
|
|
|
1939
2051
|
TODO: [🧠] Is there a better implementation?
|
|
1940
2052
|
> const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
1941
2053
|
> for (const propertyName of propertyNames) {
|
|
1942
|
-
> const value = (objectValue as
|
|
2054
|
+
> const value = (objectValue as chococake)[propertyName];
|
|
1943
2055
|
> if (value && typeof value === 'object') {
|
|
1944
2056
|
> deepClone(value);
|
|
1945
2057
|
> }
|
|
@@ -2792,7 +2904,7 @@ class DatabaseError extends Error {
|
|
|
2792
2904
|
}
|
|
2793
2905
|
}
|
|
2794
2906
|
/**
|
|
2795
|
-
* TODO:
|
|
2907
|
+
* TODO: [🐱🚀] Explain that NotFoundError ([🐱🚀] and other specific errors) has priority over DatabaseError in some contexts
|
|
2796
2908
|
*/
|
|
2797
2909
|
|
|
2798
2910
|
/**
|
|
@@ -5061,87 +5173,6 @@ async function preparePipeline(pipeline, tools, options) {
|
|
|
5061
5173
|
* @see https://docs.anthropic.com/en/docs/test-and-evaluate/strengthen-guardrails/increase-consistency#specify-the-desired-output-format
|
|
5062
5174
|
*/
|
|
5063
5175
|
|
|
5064
|
-
/**
|
|
5065
|
-
* Format either small or big number
|
|
5066
|
-
*
|
|
5067
|
-
* @public exported from `@promptbook/utils`
|
|
5068
|
-
*/
|
|
5069
|
-
function numberToString(value) {
|
|
5070
|
-
if (value === 0) {
|
|
5071
|
-
return '0';
|
|
5072
|
-
}
|
|
5073
|
-
else if (Number.isNaN(value)) {
|
|
5074
|
-
return VALUE_STRINGS.nan;
|
|
5075
|
-
}
|
|
5076
|
-
else if (value === Infinity) {
|
|
5077
|
-
return VALUE_STRINGS.infinity;
|
|
5078
|
-
}
|
|
5079
|
-
else if (value === -Infinity) {
|
|
5080
|
-
return VALUE_STRINGS.negativeInfinity;
|
|
5081
|
-
}
|
|
5082
|
-
for (let exponent = 0; exponent < 15; exponent++) {
|
|
5083
|
-
const factor = 10 ** exponent;
|
|
5084
|
-
const valueRounded = Math.round(value * factor) / factor;
|
|
5085
|
-
if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
|
|
5086
|
-
return valueRounded.toFixed(exponent);
|
|
5087
|
-
}
|
|
5088
|
-
}
|
|
5089
|
-
return value.toString();
|
|
5090
|
-
}
|
|
5091
|
-
|
|
5092
|
-
/**
|
|
5093
|
-
* Function `valueToString` will convert the given value to string
|
|
5094
|
-
* This is useful and used in the `templateParameters` function
|
|
5095
|
-
*
|
|
5096
|
-
* Note: This function is not just calling `toString` method
|
|
5097
|
-
* It's more complex and can handle this conversion specifically for LLM models
|
|
5098
|
-
* See `VALUE_STRINGS`
|
|
5099
|
-
*
|
|
5100
|
-
* Note: There are 2 similar functions
|
|
5101
|
-
* - `valueToString` converts value to string for LLM models as human-readable string
|
|
5102
|
-
* - `asSerializable` converts value to string to preserve full information to be able to convert it back
|
|
5103
|
-
*
|
|
5104
|
-
* @public exported from `@promptbook/utils`
|
|
5105
|
-
*/
|
|
5106
|
-
function valueToString(value) {
|
|
5107
|
-
try {
|
|
5108
|
-
if (value === '') {
|
|
5109
|
-
return VALUE_STRINGS.empty;
|
|
5110
|
-
}
|
|
5111
|
-
else if (value === null) {
|
|
5112
|
-
return VALUE_STRINGS.null;
|
|
5113
|
-
}
|
|
5114
|
-
else if (value === undefined) {
|
|
5115
|
-
return VALUE_STRINGS.undefined;
|
|
5116
|
-
}
|
|
5117
|
-
else if (typeof value === 'string') {
|
|
5118
|
-
return value;
|
|
5119
|
-
}
|
|
5120
|
-
else if (typeof value === 'number') {
|
|
5121
|
-
return numberToString(value);
|
|
5122
|
-
}
|
|
5123
|
-
else if (value instanceof Date) {
|
|
5124
|
-
return value.toISOString();
|
|
5125
|
-
}
|
|
5126
|
-
else {
|
|
5127
|
-
try {
|
|
5128
|
-
return JSON.stringify(value);
|
|
5129
|
-
}
|
|
5130
|
-
catch (error) {
|
|
5131
|
-
if (error instanceof TypeError && error.message.includes('circular structure')) {
|
|
5132
|
-
return VALUE_STRINGS.circular;
|
|
5133
|
-
}
|
|
5134
|
-
throw error;
|
|
5135
|
-
}
|
|
5136
|
-
}
|
|
5137
|
-
}
|
|
5138
|
-
catch (error) {
|
|
5139
|
-
assertsError(error);
|
|
5140
|
-
console.error(error);
|
|
5141
|
-
return VALUE_STRINGS.unserializable;
|
|
5142
|
-
}
|
|
5143
|
-
}
|
|
5144
|
-
|
|
5145
5176
|
/**
|
|
5146
5177
|
* Parses the given script and returns the list of all used variables that are not defined in the script
|
|
5147
5178
|
*
|
|
@@ -8068,6 +8099,60 @@ class MemoryCommitmentDefinition extends BaseCommitmentDefinition {
|
|
|
8068
8099
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
8069
8100
|
*/
|
|
8070
8101
|
|
|
8102
|
+
/**
|
|
8103
|
+
* INITIAL MESSAGE commitment definition
|
|
8104
|
+
*
|
|
8105
|
+
* The INITIAL MESSAGE commitment defines the first message that the user sees when opening the chat.
|
|
8106
|
+
* It is used to greet the user and set the tone of the conversation.
|
|
8107
|
+
*
|
|
8108
|
+
* Example usage in agent source:
|
|
8109
|
+
*
|
|
8110
|
+
* ```book
|
|
8111
|
+
* INITIAL MESSAGE Hello! I am ready to help you with your tasks.
|
|
8112
|
+
* ```
|
|
8113
|
+
*
|
|
8114
|
+
* @private [🪔] Maybe export the commitments through some package
|
|
8115
|
+
*/
|
|
8116
|
+
class InitialMessageCommitmentDefinition extends BaseCommitmentDefinition {
|
|
8117
|
+
constructor() {
|
|
8118
|
+
super('INITIAL MESSAGE');
|
|
8119
|
+
}
|
|
8120
|
+
/**
|
|
8121
|
+
* Short one-line description of INITIAL MESSAGE.
|
|
8122
|
+
*/
|
|
8123
|
+
get description() {
|
|
8124
|
+
return 'Defines the **initial message** shown to the user when the chat starts.';
|
|
8125
|
+
}
|
|
8126
|
+
/**
|
|
8127
|
+
* Markdown documentation for INITIAL MESSAGE commitment.
|
|
8128
|
+
*/
|
|
8129
|
+
get documentation() {
|
|
8130
|
+
return spaceTrim$2(`
|
|
8131
|
+
# ${this.type}
|
|
8132
|
+
|
|
8133
|
+
Defines the first message that the user sees when opening the chat. This message is purely for display purposes in the UI and does not inherently become part of the LLM's system prompt context (unless also included via other means).
|
|
8134
|
+
|
|
8135
|
+
## Key aspects
|
|
8136
|
+
|
|
8137
|
+
- Used to greet the user.
|
|
8138
|
+
- Sets the tone of the conversation.
|
|
8139
|
+
- Displayed immediately when the chat interface loads.
|
|
8140
|
+
|
|
8141
|
+
## Examples
|
|
8142
|
+
|
|
8143
|
+
\`\`\`book
|
|
8144
|
+
Support Agent
|
|
8145
|
+
|
|
8146
|
+
PERSONA You are a helpful support agent.
|
|
8147
|
+
INITIAL MESSAGE Hi there! How can I assist you today?
|
|
8148
|
+
\`\`\`
|
|
8149
|
+
`);
|
|
8150
|
+
}
|
|
8151
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
8152
|
+
return requirements;
|
|
8153
|
+
}
|
|
8154
|
+
}
|
|
8155
|
+
|
|
8071
8156
|
/**
|
|
8072
8157
|
* MESSAGE commitment definition
|
|
8073
8158
|
*
|
|
@@ -9229,6 +9314,7 @@ const COMMITMENT_REGISTRY = [
|
|
|
9229
9314
|
new NoteCommitmentDefinition('NONCE'),
|
|
9230
9315
|
new GoalCommitmentDefinition('GOAL'),
|
|
9231
9316
|
new GoalCommitmentDefinition('GOALS'),
|
|
9317
|
+
new InitialMessageCommitmentDefinition(),
|
|
9232
9318
|
new MessageCommitmentDefinition('MESSAGE'),
|
|
9233
9319
|
new MessageCommitmentDefinition('MESSAGES'),
|
|
9234
9320
|
new ScenarioCommitmentDefinition('SCENARIO'),
|
|
@@ -10117,13 +10203,31 @@ function parseAgentSource(agentSource) {
|
|
|
10117
10203
|
}
|
|
10118
10204
|
personaDescription += commitment.content;
|
|
10119
10205
|
}
|
|
10206
|
+
let initialMessage = null;
|
|
10207
|
+
for (const commitment of parseResult.commitments) {
|
|
10208
|
+
if (commitment.type !== 'INITIAL MESSAGE') {
|
|
10209
|
+
continue;
|
|
10210
|
+
}
|
|
10211
|
+
// Note: Initial message override logic - later overrides earlier
|
|
10212
|
+
// Or should it append? Usually initial message is just one block.
|
|
10213
|
+
// Let's stick to "later overrides earlier" for simplicity, or just take the last one.
|
|
10214
|
+
initialMessage = commitment.content;
|
|
10215
|
+
}
|
|
10120
10216
|
const meta = {};
|
|
10217
|
+
const links = [];
|
|
10121
10218
|
for (const commitment of parseResult.commitments) {
|
|
10219
|
+
if (commitment.type === 'META LINK') {
|
|
10220
|
+
links.push(spaceTrim$1(commitment.content));
|
|
10221
|
+
continue;
|
|
10222
|
+
}
|
|
10122
10223
|
if (commitment.type !== 'META') {
|
|
10123
10224
|
continue;
|
|
10124
10225
|
}
|
|
10125
10226
|
// Parse META commitments - format is "META TYPE content"
|
|
10126
10227
|
const metaTypeRaw = commitment.content.split(' ')[0] || 'NONE';
|
|
10228
|
+
if (metaTypeRaw === 'LINK') {
|
|
10229
|
+
links.push(spaceTrim$1(commitment.content.substring(metaTypeRaw.length)));
|
|
10230
|
+
}
|
|
10127
10231
|
const metaType = normalizeTo_camelCase(metaTypeRaw);
|
|
10128
10232
|
meta[metaType] = spaceTrim$1(commitment.content.substring(metaTypeRaw.length));
|
|
10129
10233
|
}
|
|
@@ -10139,7 +10243,9 @@ function parseAgentSource(agentSource) {
|
|
|
10139
10243
|
agentName: normalizeAgentName(parseResult.agentName || createDefaultAgentName(agentSource)),
|
|
10140
10244
|
agentHash,
|
|
10141
10245
|
personaDescription,
|
|
10246
|
+
initialMessage,
|
|
10142
10247
|
meta,
|
|
10248
|
+
links,
|
|
10143
10249
|
parameters,
|
|
10144
10250
|
};
|
|
10145
10251
|
}
|
|
@@ -10310,26 +10416,28 @@ const DEFAULT_BOOK = padBook(validateBook(spaceTrim$1(`
|
|
|
10310
10416
|
PERSONA A friendly AI assistant that helps you with your tasks
|
|
10311
10417
|
`)));
|
|
10312
10418
|
// <- Note: Not using book`...` notation to avoid strange error in jest unit tests `TypeError: (0 , book_notation_1.book) is not a function`
|
|
10313
|
-
// <- TODO:
|
|
10314
|
-
// <-
|
|
10315
|
-
// <- TODO:
|
|
10419
|
+
// <- TODO: [🐱🚀] `GENESIS_BOOK` / `ADAM_BOOK` in `/agents/adam.book`
|
|
10420
|
+
// <- [🐱🚀] Buttons into genesis book
|
|
10421
|
+
// <- TODO: [🐱🚀] generateBookBoilerplate and deprecate `DEFAULT_BOOK`
|
|
10316
10422
|
|
|
10423
|
+
// import { getTableName } from '../../../../../apps/agents-server/src/database/getTableName';
|
|
10424
|
+
// <- TODO: [🐱🚀] Prevent imports from `/apps` -> `/src`
|
|
10317
10425
|
/**
|
|
10318
10426
|
* Agent collection stored in Supabase table
|
|
10319
10427
|
*
|
|
10320
10428
|
* Note: This object can work both from Node.js and browser environment depending on the Supabase client provided
|
|
10321
10429
|
*
|
|
10322
10430
|
* @public exported from `@promptbook/core`
|
|
10323
|
-
* <- TODO:
|
|
10431
|
+
* <- TODO: [🐱🚀] Move to `@promptbook/supabase` package
|
|
10324
10432
|
*/
|
|
10325
|
-
class AgentCollectionInSupabase /* TODO:
|
|
10433
|
+
class AgentCollectionInSupabase /* TODO: [🐱🚀] implements Agent */ {
|
|
10326
10434
|
/**
|
|
10327
10435
|
* @param rootPath - path to the directory with agents
|
|
10328
|
-
* @param tools - Execution tools to be used in
|
|
10436
|
+
* @param tools - Execution tools to be used in [🐱🚀] `Agent` itself and listing the agents
|
|
10329
10437
|
* @param options - Options for the collection creation
|
|
10330
10438
|
*/
|
|
10331
10439
|
constructor(supabaseClient,
|
|
10332
|
-
/// TODO:
|
|
10440
|
+
/// TODO: [🐱🚀] Remove> private readonly tools?: Pick<ExecutionTools, 'llm' | 'fs' | 'scrapers'>,
|
|
10333
10441
|
options) {
|
|
10334
10442
|
this.supabaseClient = supabaseClient;
|
|
10335
10443
|
this.options = options;
|
|
@@ -10343,7 +10451,9 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10343
10451
|
*/
|
|
10344
10452
|
async listAgents( /* TODO: [🧠] Allow to pass some condition here */) {
|
|
10345
10453
|
const { isVerbose = DEFAULT_IS_VERBOSE } = this.options || {};
|
|
10346
|
-
const selectResult = await this.supabaseClient
|
|
10454
|
+
const selectResult = await this.supabaseClient
|
|
10455
|
+
.from(this.getTableName('Agent'))
|
|
10456
|
+
.select('agentName,agentProfile');
|
|
10347
10457
|
if (selectResult.error) {
|
|
10348
10458
|
throw new DatabaseError(spaceTrim((block) => `
|
|
10349
10459
|
|
|
@@ -10371,29 +10481,27 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10371
10481
|
});
|
|
10372
10482
|
}
|
|
10373
10483
|
/**
|
|
10374
|
-
*
|
|
10484
|
+
* [🐱🚀]@@@
|
|
10375
10485
|
*/
|
|
10376
10486
|
async getAgentSource(agentName) {
|
|
10377
10487
|
const selectResult = await this.supabaseClient
|
|
10378
|
-
.from('Agent')
|
|
10488
|
+
.from(this.getTableName('Agent'))
|
|
10379
10489
|
.select('agentSource')
|
|
10380
|
-
.eq('agentName', agentName)
|
|
10381
|
-
|
|
10382
|
-
/*
|
|
10383
|
-
if (selectResult.data===null) {
|
|
10490
|
+
.eq('agentName', agentName);
|
|
10491
|
+
if (selectResult.data && selectResult.data.length === 0) {
|
|
10384
10492
|
throw new NotFoundError(`Agent "${agentName}" not found`);
|
|
10385
10493
|
}
|
|
10386
|
-
|
|
10387
|
-
|
|
10494
|
+
else if (selectResult.data && selectResult.data.length > 1) {
|
|
10495
|
+
throw new UnexpectedError(`More agents with agentName="${agentName}" found`);
|
|
10496
|
+
}
|
|
10497
|
+
else if (selectResult.error) {
|
|
10388
10498
|
throw new DatabaseError(spaceTrim((block) => `
|
|
10389
|
-
|
|
10390
10499
|
Error fetching agent "${agentName}" from Supabase:
|
|
10391
10500
|
|
|
10392
10501
|
${block(selectResult.error.message)}
|
|
10393
10502
|
`));
|
|
10394
|
-
// <- TODO: !!! First check if the error is "not found" and throw `NotFoundError` instead then throw `DatabaseError`
|
|
10395
10503
|
}
|
|
10396
|
-
return selectResult.data.agentSource;
|
|
10504
|
+
return selectResult.data[0].agentSource;
|
|
10397
10505
|
}
|
|
10398
10506
|
/**
|
|
10399
10507
|
* Creates a new agent in the collection
|
|
@@ -10404,7 +10512,7 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10404
10512
|
const agentProfile = parseAgentSource(agentSource);
|
|
10405
10513
|
// <- TODO: [🕛]
|
|
10406
10514
|
const { agentName, agentHash } = agentProfile;
|
|
10407
|
-
const insertAgentResult = await this.supabaseClient.from('Agent').insert({
|
|
10515
|
+
const insertAgentResult = await this.supabaseClient.from(this.getTableName('Agent')).insert({
|
|
10408
10516
|
agentName,
|
|
10409
10517
|
agentHash,
|
|
10410
10518
|
agentProfile,
|
|
@@ -10421,7 +10529,7 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10421
10529
|
${block(insertAgentResult.error.message)}
|
|
10422
10530
|
`));
|
|
10423
10531
|
}
|
|
10424
|
-
await this.supabaseClient.from('AgentHistory').insert({
|
|
10532
|
+
await this.supabaseClient.from(this.getTableName('AgentHistory')).insert({
|
|
10425
10533
|
createdAt: new Date().toISOString(),
|
|
10426
10534
|
agentName,
|
|
10427
10535
|
agentHash,
|
|
@@ -10437,7 +10545,7 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10437
10545
|
*/
|
|
10438
10546
|
async updateAgentSource(agentName, agentSource) {
|
|
10439
10547
|
const selectPreviousAgentResult = await this.supabaseClient
|
|
10440
|
-
.from('Agent')
|
|
10548
|
+
.from(this.getTableName('Agent'))
|
|
10441
10549
|
.select('agentHash,agentName')
|
|
10442
10550
|
.eq('agentName', agentName)
|
|
10443
10551
|
.single();
|
|
@@ -10448,7 +10556,7 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10448
10556
|
|
|
10449
10557
|
${block(selectPreviousAgentResult.error.message)}
|
|
10450
10558
|
`));
|
|
10451
|
-
// <- TODO:
|
|
10559
|
+
// <- TODO: [🐱🚀] First check if the error is "not found" and throw `NotFoundError` instead then throw `DatabaseError`, look at `getAgentSource` implementation
|
|
10452
10560
|
}
|
|
10453
10561
|
selectPreviousAgentResult.data.agentName;
|
|
10454
10562
|
const previousAgentHash = selectPreviousAgentResult.data.agentHash;
|
|
@@ -10456,9 +10564,9 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10456
10564
|
// <- TODO: [🕛]
|
|
10457
10565
|
const { agentHash } = agentProfile;
|
|
10458
10566
|
const updateAgentResult = await this.supabaseClient
|
|
10459
|
-
.from('Agent')
|
|
10567
|
+
.from(this.getTableName('Agent'))
|
|
10460
10568
|
.update({
|
|
10461
|
-
// TODO:
|
|
10569
|
+
// TODO: [🐱🚀] Compare not update> agentName: agentProfile.agentName || '[🐱🚀]' /* <- TODO: [🐱🚀] Remove */,
|
|
10462
10570
|
agentProfile,
|
|
10463
10571
|
updatedAt: new Date().toISOString(),
|
|
10464
10572
|
agentHash: agentProfile.agentHash,
|
|
@@ -10466,9 +10574,9 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10466
10574
|
promptbookEngineVersion: PROMPTBOOK_ENGINE_VERSION,
|
|
10467
10575
|
})
|
|
10468
10576
|
.eq('agentName', agentName);
|
|
10469
|
-
// console.log('
|
|
10470
|
-
// console.log('
|
|
10471
|
-
// console.log('
|
|
10577
|
+
// console.log('[🐱🚀] updateAgent', updateResult);
|
|
10578
|
+
// console.log('[🐱🚀] old', oldAgentSource);
|
|
10579
|
+
// console.log('[🐱🚀] new', newAgentSource);
|
|
10472
10580
|
if (updateAgentResult.error) {
|
|
10473
10581
|
throw new DatabaseError(spaceTrim((block) => `
|
|
10474
10582
|
Error updating agent "${agentName}" in Supabase:
|
|
@@ -10476,7 +10584,7 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10476
10584
|
${block(updateAgentResult.error.message)}
|
|
10477
10585
|
`));
|
|
10478
10586
|
}
|
|
10479
|
-
await this.supabaseClient.from('AgentHistory').insert({
|
|
10587
|
+
await this.supabaseClient.from(this.getTableName('AgentHistory')).insert({
|
|
10480
10588
|
createdAt: new Date().toISOString(),
|
|
10481
10589
|
agentName,
|
|
10482
10590
|
agentHash,
|
|
@@ -10486,7 +10594,7 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10486
10594
|
});
|
|
10487
10595
|
// <- TODO: [🧠] What to do with `insertAgentHistoryResult.error`, ignore? wait?
|
|
10488
10596
|
}
|
|
10489
|
-
// TODO:
|
|
10597
|
+
// TODO: [🐱🚀] public async getAgentSourceSubject(agentName: string_agent_name): Promise<BehaviorSubject<string_book>>
|
|
10490
10598
|
// Use Supabase realtime logic
|
|
10491
10599
|
/**
|
|
10492
10600
|
* Deletes an agent from the collection
|
|
@@ -10494,9 +10602,19 @@ class AgentCollectionInSupabase /* TODO: !!!!!! implements Agent */ {
|
|
|
10494
10602
|
async deleteAgent(agentName) {
|
|
10495
10603
|
throw new NotYetImplementedError('Method not implemented.');
|
|
10496
10604
|
}
|
|
10605
|
+
/**
|
|
10606
|
+
* Get the Supabase table name with prefix
|
|
10607
|
+
*
|
|
10608
|
+
* @param tableName - The original table name
|
|
10609
|
+
* @returns The prefixed table name
|
|
10610
|
+
*/
|
|
10611
|
+
getTableName(tableName) {
|
|
10612
|
+
const { tablePrefix = '' } = this.options || {};
|
|
10613
|
+
return `${tablePrefix}${tableName}`;
|
|
10614
|
+
}
|
|
10497
10615
|
}
|
|
10498
10616
|
/**
|
|
10499
|
-
* TODO:
|
|
10617
|
+
* TODO: [🐱🚀] Implement it here correctly and update JSDoc comments here, and on interface + other implementations
|
|
10500
10618
|
* TODO: Write unit test
|
|
10501
10619
|
* TODO: [🧠][🚙] `AgentXxx` vs `AgentsXxx` naming convention
|
|
10502
10620
|
*/
|
|
@@ -16651,11 +16769,12 @@ class OpenAiExecutionTools extends OpenAiCompatibleExecutionTools {
|
|
|
16651
16769
|
*
|
|
16652
16770
|
* This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
|
|
16653
16771
|
*
|
|
16654
|
-
*
|
|
16772
|
+
* Note: [🦖] There are several different things in Promptbook:
|
|
16655
16773
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
16656
16774
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
16657
16775
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
16658
16776
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
16777
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
16659
16778
|
*
|
|
16660
16779
|
* @public exported from `@promptbook/openai`
|
|
16661
16780
|
*/
|
|
@@ -16690,6 +16809,12 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
16690
16809
|
* Calls OpenAI API to use a chat model.
|
|
16691
16810
|
*/
|
|
16692
16811
|
async callChatModel(prompt) {
|
|
16812
|
+
return this.callChatModelStream(prompt, () => { });
|
|
16813
|
+
}
|
|
16814
|
+
/**
|
|
16815
|
+
* Calls OpenAI API to use a chat model with streaming.
|
|
16816
|
+
*/
|
|
16817
|
+
async callChatModelStream(prompt, onProgress) {
|
|
16693
16818
|
var _a, _b, _c;
|
|
16694
16819
|
if (this.options.isVerbose) {
|
|
16695
16820
|
console.info('💬 OpenAI callChatModel call', { prompt });
|
|
@@ -16757,21 +16882,24 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
16757
16882
|
console.info('connect', stream.currentEvent);
|
|
16758
16883
|
}
|
|
16759
16884
|
});
|
|
16760
|
-
|
|
16761
|
-
|
|
16762
|
-
|
|
16763
|
-
this.options.isVerbose &&
|
|
16764
|
-
messageDelta &&
|
|
16765
|
-
messageDelta.content &&
|
|
16766
|
-
messageDelta.content[0] &&
|
|
16767
|
-
messageDelta.content[0].type === 'text'
|
|
16768
|
-
) {
|
|
16769
|
-
console.info('messageDelta', messageDelta.content[0].text?.value);
|
|
16885
|
+
stream.on('textDelta', (textDelta, snapshot) => {
|
|
16886
|
+
if (this.options.isVerbose && textDelta.value) {
|
|
16887
|
+
console.info('textDelta', textDelta.value);
|
|
16770
16888
|
}
|
|
16771
|
-
|
|
16772
|
-
|
|
16889
|
+
const chunk = {
|
|
16890
|
+
content: textDelta.value || '',
|
|
16891
|
+
modelName: 'assistant',
|
|
16892
|
+
timing: {
|
|
16893
|
+
start,
|
|
16894
|
+
complete: $getCurrentDate(),
|
|
16895
|
+
},
|
|
16896
|
+
usage: UNCERTAIN_USAGE,
|
|
16897
|
+
rawPromptContent,
|
|
16898
|
+
rawRequest,
|
|
16899
|
+
rawResponse: snapshot,
|
|
16900
|
+
};
|
|
16901
|
+
onProgress(chunk);
|
|
16773
16902
|
});
|
|
16774
|
-
*/
|
|
16775
16903
|
stream.on('messageCreated', (message) => {
|
|
16776
16904
|
if (this.options.isVerbose) {
|
|
16777
16905
|
console.info('messageCreated', message);
|
|
@@ -16807,7 +16935,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
16807
16935
|
}
|
|
16808
16936
|
return exportJson({
|
|
16809
16937
|
name: 'promptResult',
|
|
16810
|
-
message: `Result of \`OpenAiAssistantExecutionTools.
|
|
16938
|
+
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
|
|
16811
16939
|
order: [],
|
|
16812
16940
|
value: {
|
|
16813
16941
|
content: resultContent,
|
|
@@ -16946,9 +17074,9 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
16946
17074
|
}
|
|
16947
17075
|
const assistant = await client.beta.assistants.create(assistantConfig);
|
|
16948
17076
|
console.log(`✅ Assistant created: ${assistant.id}`);
|
|
16949
|
-
// TODO:
|
|
16950
|
-
// TODO:
|
|
16951
|
-
// TODO:
|
|
17077
|
+
// TODO: [🐱🚀] Try listing existing assistants
|
|
17078
|
+
// TODO: [🐱🚀] Try marking existing assistants by DISCRIMINANT
|
|
17079
|
+
// TODO: [🐱🚀] Allow to update and reconnect to existing assistants
|
|
16952
17080
|
return new OpenAiAssistantExecutionTools({
|
|
16953
17081
|
...this.options,
|
|
16954
17082
|
isCreatingNewAssistantsAllowed: false,
|
|
@@ -17076,11 +17204,12 @@ const DISCRIMINANT = 'OPEN_AI_ASSISTANT_V1';
|
|
|
17076
17204
|
* Execution Tools for calling LLM models with a predefined agent "soul"
|
|
17077
17205
|
* This wraps underlying LLM execution tools and applies agent-specific system prompts and requirements
|
|
17078
17206
|
*
|
|
17079
|
-
*
|
|
17207
|
+
* Note: [🦖] There are several different things in Promptbook:
|
|
17080
17208
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
17081
17209
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
17082
17210
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
17083
17211
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
17212
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
17084
17213
|
*
|
|
17085
17214
|
* @public exported from `@promptbook/core`
|
|
17086
17215
|
*/
|
|
@@ -17102,6 +17231,16 @@ class AgentLlmExecutionTools {
|
|
|
17102
17231
|
*/
|
|
17103
17232
|
this._cachedAgentInfo = null;
|
|
17104
17233
|
}
|
|
17234
|
+
/**
|
|
17235
|
+
* Updates the agent source and clears the cache
|
|
17236
|
+
*
|
|
17237
|
+
* @param agentSource The new agent source string
|
|
17238
|
+
*/
|
|
17239
|
+
updateAgentSource(agentSource) {
|
|
17240
|
+
this.options.agentSource = agentSource;
|
|
17241
|
+
this._cachedAgentInfo = null;
|
|
17242
|
+
this._cachedModelRequirements = null;
|
|
17243
|
+
}
|
|
17105
17244
|
/**
|
|
17106
17245
|
* Get cached or parse agent information
|
|
17107
17246
|
*/
|
|
@@ -17174,9 +17313,12 @@ class AgentLlmExecutionTools {
|
|
|
17174
17313
|
* Calls the chat model with agent-specific system prompt and requirements
|
|
17175
17314
|
*/
|
|
17176
17315
|
async callChatModel(prompt) {
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17316
|
+
return this.callChatModelStream(prompt, () => { });
|
|
17317
|
+
}
|
|
17318
|
+
/**
|
|
17319
|
+
* Calls the chat model with agent-specific system prompt and requirements with streaming
|
|
17320
|
+
*/
|
|
17321
|
+
async callChatModelStream(prompt, onProgress) {
|
|
17180
17322
|
// Ensure we're working with a chat prompt
|
|
17181
17323
|
if (prompt.modelRequirements.modelVariant !== 'CHAT') {
|
|
17182
17324
|
throw new Error('AgentLlmExecutionTools only supports chat prompts');
|
|
@@ -17215,7 +17357,7 @@ class AgentLlmExecutionTools {
|
|
|
17215
17357
|
if (this.options.isVerbose) {
|
|
17216
17358
|
console.log(`1️⃣ Creating new OpenAI Assistant for agent ${this.title}...`);
|
|
17217
17359
|
}
|
|
17218
|
-
// <- TODO:
|
|
17360
|
+
// <- TODO: [🐱🚀] Check also `isCreatingNewAssistantsAllowed` and warn about it
|
|
17219
17361
|
assistant = await this.options.llmTools.createNewAssistant({
|
|
17220
17362
|
name: this.title,
|
|
17221
17363
|
instructions: modelRequirements.systemMessage,
|
|
@@ -17232,7 +17374,7 @@ class AgentLlmExecutionTools {
|
|
|
17232
17374
|
requirementsHash,
|
|
17233
17375
|
});
|
|
17234
17376
|
}
|
|
17235
|
-
underlyingLlmResult = await assistant.
|
|
17377
|
+
underlyingLlmResult = await assistant.callChatModelStream(chatPrompt, onProgress);
|
|
17236
17378
|
}
|
|
17237
17379
|
else {
|
|
17238
17380
|
if (this.options.isVerbose) {
|
|
@@ -17251,7 +17393,16 @@ class AgentLlmExecutionTools {
|
|
|
17251
17393
|
: ''),
|
|
17252
17394
|
},
|
|
17253
17395
|
};
|
|
17254
|
-
|
|
17396
|
+
if (this.options.llmTools.callChatModelStream) {
|
|
17397
|
+
underlyingLlmResult = await this.options.llmTools.callChatModelStream(modifiedChatPrompt, onProgress);
|
|
17398
|
+
}
|
|
17399
|
+
else if (this.options.llmTools.callChatModel) {
|
|
17400
|
+
underlyingLlmResult = await this.options.llmTools.callChatModel(modifiedChatPrompt);
|
|
17401
|
+
onProgress(underlyingLlmResult);
|
|
17402
|
+
}
|
|
17403
|
+
else {
|
|
17404
|
+
throw new Error('Underlying LLM execution tools do not support chat model calls');
|
|
17405
|
+
}
|
|
17255
17406
|
}
|
|
17256
17407
|
let content = underlyingLlmResult.content;
|
|
17257
17408
|
// Note: Cleanup the AI artifacts from the content
|
|
@@ -17278,11 +17429,12 @@ AgentLlmExecutionTools.assistantCache = new Map();
|
|
|
17278
17429
|
/**
|
|
17279
17430
|
* Represents one AI Agent
|
|
17280
17431
|
*
|
|
17281
|
-
*
|
|
17432
|
+
* Note: [🦖] There are several different things in Promptbook:
|
|
17282
17433
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
17283
17434
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
17284
17435
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
17285
17436
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
17437
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
17286
17438
|
*
|
|
17287
17439
|
* @public exported from `@promptbook/core`
|
|
17288
17440
|
*/
|
|
@@ -17312,31 +17464,70 @@ class Agent extends AgentLlmExecutionTools {
|
|
|
17312
17464
|
super({
|
|
17313
17465
|
isVerbose: options.isVerbose,
|
|
17314
17466
|
llmTools: getSingleLlmExecutionTools(options.executionTools.llm),
|
|
17315
|
-
agentSource: agentSource.value, // <- TODO:
|
|
17467
|
+
agentSource: agentSource.value, // <- TODO: [🐱🚀] Allow to pass BehaviorSubject<string_book> OR refresh llmExecutionTools.callChat on agentSource change
|
|
17316
17468
|
});
|
|
17317
17469
|
this._agentName = undefined;
|
|
17318
17470
|
/**
|
|
17319
17471
|
* Description of the agent
|
|
17320
17472
|
*/
|
|
17321
17473
|
this.personaDescription = null;
|
|
17474
|
+
/**
|
|
17475
|
+
* The initial message shown to the user when the chat starts
|
|
17476
|
+
*/
|
|
17477
|
+
this.initialMessage = null;
|
|
17478
|
+
/**
|
|
17479
|
+
* Links found in the agent source
|
|
17480
|
+
*/
|
|
17481
|
+
this.links = [];
|
|
17322
17482
|
/**
|
|
17323
17483
|
* Metadata like image or color
|
|
17324
17484
|
*/
|
|
17325
17485
|
this.meta = {};
|
|
17326
|
-
// TODO:
|
|
17327
|
-
// TODO:
|
|
17486
|
+
// TODO: [🐱🚀] Add `Agent` simple "mocked" learning by appending to agent source
|
|
17487
|
+
// TODO: [🐱🚀] Add `Agent` learning by promptbookAgent
|
|
17328
17488
|
this.agentSource = agentSource;
|
|
17329
17489
|
this.agentSource.subscribe((source) => {
|
|
17330
|
-
|
|
17490
|
+
this.updateAgentSource(source);
|
|
17491
|
+
const { agentName, personaDescription, initialMessage, links, meta } = parseAgentSource(source);
|
|
17331
17492
|
this._agentName = agentName;
|
|
17332
17493
|
this.personaDescription = personaDescription;
|
|
17494
|
+
this.initialMessage = initialMessage;
|
|
17495
|
+
this.links = links;
|
|
17333
17496
|
this.meta = { ...this.meta, ...meta };
|
|
17334
17497
|
});
|
|
17335
17498
|
}
|
|
17499
|
+
/**
|
|
17500
|
+
* Calls the chat model with agent-specific system prompt and requirements with streaming
|
|
17501
|
+
*
|
|
17502
|
+
* Note: This method also implements the learning mechanism
|
|
17503
|
+
*/
|
|
17504
|
+
async callChatModelStream(prompt, onProgress) {
|
|
17505
|
+
const result = await super.callChatModelStream(prompt, onProgress);
|
|
17506
|
+
// TODO: !!! Extract learning to separate method
|
|
17507
|
+
// Learning: Append the conversation sample to the agent source
|
|
17508
|
+
const learningExample = spaceTrim$1((block) => `
|
|
17509
|
+
|
|
17510
|
+
---
|
|
17511
|
+
|
|
17512
|
+
SAMPLE
|
|
17513
|
+
|
|
17514
|
+
User:
|
|
17515
|
+
${block(prompt.content)}
|
|
17516
|
+
|
|
17517
|
+
${this.title} (Me, the Agent):
|
|
17518
|
+
${block(result.content)}
|
|
17519
|
+
|
|
17520
|
+
`);
|
|
17521
|
+
// Append to the current source
|
|
17522
|
+
const currentSource = this.agentSource.value;
|
|
17523
|
+
const newSource = padBook(validateBook(spaceTrim$1(currentSource) + '\n\n' + learningExample));
|
|
17524
|
+
// Update the source (which will trigger the subscription and update the underlying tools)
|
|
17525
|
+
this.agentSource.next(newSource);
|
|
17526
|
+
return result;
|
|
17527
|
+
}
|
|
17336
17528
|
}
|
|
17337
17529
|
/**
|
|
17338
17530
|
* TODO: [🧠][😰]Agent is not working with the parameters, should it be?
|
|
17339
|
-
* TODO: !!! Agent on remote server
|
|
17340
17531
|
*/
|
|
17341
17532
|
|
|
17342
17533
|
/**
|
|
@@ -17402,25 +17593,31 @@ const _AgentRegistration = $llmToolsRegister.register(createAgentLlmExecutionToo
|
|
|
17402
17593
|
/**
|
|
17403
17594
|
* Represents one AI Agent
|
|
17404
17595
|
*
|
|
17405
|
-
*
|
|
17596
|
+
* Note: [🦖] There are several different things in Promptbook:
|
|
17406
17597
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
17407
|
-
* !!!!!! `RemoteAgent`
|
|
17408
17598
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
17409
17599
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
17410
17600
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
17601
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
17411
17602
|
*
|
|
17412
17603
|
* @public exported from `@promptbook/core`
|
|
17413
17604
|
*/
|
|
17414
17605
|
class RemoteAgent extends Agent {
|
|
17415
17606
|
static async connect(options) {
|
|
17416
|
-
console.log('
|
|
17417
|
-
const
|
|
17418
|
-
// <- TODO:
|
|
17419
|
-
// <- TODO:
|
|
17420
|
-
const
|
|
17421
|
-
|
|
17422
|
-
//
|
|
17423
|
-
|
|
17607
|
+
console.log('[🐱🚀]', `${options.agentUrl}/api/profile`);
|
|
17608
|
+
const profileResponse = await fetch(`${options.agentUrl}/api/profile`);
|
|
17609
|
+
// <- TODO: [🐱🚀] What about closed-source agents?
|
|
17610
|
+
// <- TODO: [🐱🚀] Maybe use promptbookFetch
|
|
17611
|
+
const profile = await profileResponse.json();
|
|
17612
|
+
// Note: We are creating dummy agent source because we don't have the source from the remote agent
|
|
17613
|
+
// But we populate the metadata from the profile
|
|
17614
|
+
const agentSource = new BehaviorSubject(`
|
|
17615
|
+
# ${profile.agentName}
|
|
17616
|
+
|
|
17617
|
+
${profile.personaDescription}
|
|
17618
|
+
`);
|
|
17619
|
+
// <- TODO: [🐱🚀] Support updating and self-updating
|
|
17620
|
+
const remoteAgent = new RemoteAgent({
|
|
17424
17621
|
...options,
|
|
17425
17622
|
executionTools: {
|
|
17426
17623
|
/* Note: These tools are not used */
|
|
@@ -17437,22 +17634,51 @@ class RemoteAgent extends Agent {
|
|
|
17437
17634
|
},
|
|
17438
17635
|
agentSource,
|
|
17439
17636
|
});
|
|
17637
|
+
remoteAgent._remoteAgentName = profile.agentName;
|
|
17638
|
+
remoteAgent._remoteAgentHash = profile.agentHash;
|
|
17639
|
+
remoteAgent.personaDescription = profile.personaDescription;
|
|
17640
|
+
remoteAgent.initialMessage = profile.initialMessage;
|
|
17641
|
+
remoteAgent.links = profile.links;
|
|
17642
|
+
remoteAgent.meta = profile.meta;
|
|
17643
|
+
return remoteAgent;
|
|
17440
17644
|
}
|
|
17441
17645
|
constructor(options) {
|
|
17442
17646
|
super(options);
|
|
17443
17647
|
this.agentUrl = options.agentUrl;
|
|
17444
17648
|
}
|
|
17649
|
+
get agentName() {
|
|
17650
|
+
return this._remoteAgentName || super.agentName;
|
|
17651
|
+
}
|
|
17652
|
+
get agentHash() {
|
|
17653
|
+
return this._remoteAgentHash || super.agentHash;
|
|
17654
|
+
}
|
|
17445
17655
|
/**
|
|
17446
17656
|
* Calls the agent on agents remote server
|
|
17447
17657
|
*/
|
|
17448
17658
|
async callChatModel(prompt) {
|
|
17659
|
+
return this.callChatModelStream(prompt, () => { });
|
|
17660
|
+
}
|
|
17661
|
+
/**
|
|
17662
|
+
* Calls the agent on agents remote server with streaming
|
|
17663
|
+
*/
|
|
17664
|
+
async callChatModelStream(prompt, onProgress) {
|
|
17449
17665
|
// Ensure we're working with a chat prompt
|
|
17450
17666
|
if (prompt.modelRequirements.modelVariant !== 'CHAT') {
|
|
17451
17667
|
throw new Error('Agents only supports chat prompts');
|
|
17452
17668
|
}
|
|
17453
|
-
const
|
|
17454
|
-
|
|
17455
|
-
|
|
17669
|
+
const chatPrompt = prompt;
|
|
17670
|
+
const bookResponse = await fetch(`${this.agentUrl}/api/chat`, {
|
|
17671
|
+
method: 'POST',
|
|
17672
|
+
headers: {
|
|
17673
|
+
'Content-Type': 'application/json',
|
|
17674
|
+
},
|
|
17675
|
+
body: JSON.stringify({
|
|
17676
|
+
message: prompt.content,
|
|
17677
|
+
thread: chatPrompt.thread,
|
|
17678
|
+
}),
|
|
17679
|
+
});
|
|
17680
|
+
// <- TODO: [🐱🚀] What about closed-source agents?
|
|
17681
|
+
// <- TODO: [🐱🚀] Maybe use promptbookFetch
|
|
17456
17682
|
let content = '';
|
|
17457
17683
|
if (!bookResponse.body) {
|
|
17458
17684
|
content = await bookResponse.text();
|
|
@@ -17471,16 +17697,37 @@ class RemoteAgent extends Agent {
|
|
|
17471
17697
|
const textChunk = decoder.decode(value, { stream: true });
|
|
17472
17698
|
// console.debug('RemoteAgent chunk:', textChunk);
|
|
17473
17699
|
content += textChunk;
|
|
17700
|
+
onProgress({
|
|
17701
|
+
content,
|
|
17702
|
+
modelName: this.modelName,
|
|
17703
|
+
timing: {},
|
|
17704
|
+
usage: {},
|
|
17705
|
+
rawPromptContent: {},
|
|
17706
|
+
rawRequest: {},
|
|
17707
|
+
rawResponse: {},
|
|
17708
|
+
});
|
|
17474
17709
|
}
|
|
17475
17710
|
}
|
|
17476
17711
|
// Flush any remaining decoder internal state
|
|
17477
|
-
|
|
17712
|
+
const lastChunk = decoder.decode();
|
|
17713
|
+
if (lastChunk) {
|
|
17714
|
+
content += lastChunk;
|
|
17715
|
+
onProgress({
|
|
17716
|
+
content: lastChunk,
|
|
17717
|
+
modelName: this.modelName,
|
|
17718
|
+
timing: {},
|
|
17719
|
+
usage: {},
|
|
17720
|
+
rawPromptContent: {},
|
|
17721
|
+
rawRequest: {},
|
|
17722
|
+
rawResponse: {},
|
|
17723
|
+
});
|
|
17724
|
+
}
|
|
17478
17725
|
}
|
|
17479
17726
|
finally {
|
|
17480
17727
|
reader.releaseLock();
|
|
17481
17728
|
}
|
|
17482
17729
|
}
|
|
17483
|
-
// <- TODO:
|
|
17730
|
+
// <- TODO: [🐱🚀] Transfer metadata
|
|
17484
17731
|
const agentResult = {
|
|
17485
17732
|
content,
|
|
17486
17733
|
modelName: this.modelName,
|
|
@@ -17489,7 +17736,7 @@ class RemoteAgent extends Agent {
|
|
|
17489
17736
|
rawPromptContent: {},
|
|
17490
17737
|
rawRequest: {},
|
|
17491
17738
|
rawResponse: {},
|
|
17492
|
-
// <- TODO:
|
|
17739
|
+
// <- TODO: [🐱🚀] Transfer and proxy the metadata
|
|
17493
17740
|
};
|
|
17494
17741
|
return agentResult;
|
|
17495
17742
|
}
|
|
@@ -18776,7 +19023,7 @@ function $generateBookBoilerplate(options) {
|
|
|
18776
19023
|
const agentSource = validateBook(spaceTrim$1((block) => `
|
|
18777
19024
|
${agentName}
|
|
18778
19025
|
|
|
18779
|
-
META COLOR ${color || '#3498db' /* <- TODO: [🧠]
|
|
19026
|
+
META COLOR ${color || '#3498db' /* <- TODO: [🧠] [🐱🚀] Best default color */}
|
|
18780
19027
|
PERSONA ${block(personaDescription)}
|
|
18781
19028
|
`));
|
|
18782
19029
|
return agentSource;
|