@promptbook/core 0.103.0-47 ā 0.103.0-49
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 +1287 -876
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +1 -0
- package/esm/typings/src/_packages/core.index.d.ts +6 -0
- package/esm/typings/src/_packages/types.index.d.ts +4 -0
- package/esm/typings/src/_packages/utils.index.d.ts +2 -0
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +17 -3
- package/esm/typings/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +2 -1
- package/esm/typings/src/book-2.0/agent-source/computeAgentHash.d.ts +8 -0
- package/esm/typings/src/book-2.0/agent-source/computeAgentHash.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/agent-source/createDefaultAgentName.d.ts +8 -0
- package/esm/typings/src/book-2.0/agent-source/normalizeAgentName.d.ts +9 -0
- package/esm/typings/src/book-2.0/agent-source/normalizeAgentName.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/agent-source/parseAgentSourceWithCommitments.d.ts +1 -1
- 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/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +57 -32
- 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/_common/utils/assertUniqueModels.d.ts +12 -0
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +17 -4
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +10 -1
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +6 -2
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +30 -4
- package/esm/typings/src/llm-providers/openai/openai-models.test.d.ts +4 -0
- package/esm/typings/src/remote-server/startAgentServer.d.ts +2 -2
- package/esm/typings/src/remote-server/startRemoteServer.d.ts +1 -2
- package/esm/typings/src/transpilers/openai-sdk/register.d.ts +1 -1
- package/esm/typings/src/types/typeAliases.d.ts +6 -0
- 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/normalization/normalize-to-kebab-case.d.ts +2 -0
- package/esm/typings/src/utils/normalization/normalizeTo_PascalCase.d.ts +3 -0
- package/esm/typings/src/utils/normalization/normalizeTo_camelCase.d.ts +2 -0
- package/esm/typings/src/utils/normalization/titleToName.d.ts +2 -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 +1291 -877
- package/umd/index.umd.js.map +1 -1
package/umd/index.umd.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', '
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-core"] = {}, global.spaceTrim$1, global.crypto, global.rxjs, global.waitasecond, global.
|
|
5
|
-
})(this, (function (exports, spaceTrim$1, crypto, rxjs, waitasecond,
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('crypto-js'), require('crypto-js/enc-hex'), require('spacetrim'), require('crypto'), require('rxjs'), require('waitasecond'), require('crypto-js/sha256'), require('path'), require('mime-types'), require('papaparse'), require('moment'), require('colors'), require('bottleneck'), require('openai')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'crypto-js', 'crypto-js/enc-hex', 'spacetrim', 'crypto', 'rxjs', 'waitasecond', 'crypto-js/sha256', 'path', 'mime-types', 'papaparse', 'moment', 'colors', 'bottleneck', 'openai'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-core"] = {}, global.cryptoJs, global.hexEncoder, global.spaceTrim$1, global.crypto, global.rxjs, global.waitasecond, global.sha256, global.path, global.mimeTypes, global.papaparse, global.moment, global.colors, global.Bottleneck, global.OpenAI));
|
|
5
|
+
})(this, (function (exports, cryptoJs, hexEncoder, spaceTrim$1, crypto, rxjs, waitasecond, sha256, path, mimeTypes, papaparse, moment, colors, Bottleneck, OpenAI) { 'use strict';
|
|
6
6
|
|
|
7
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
8
|
|
|
9
|
-
var spaceTrim__default = /*#__PURE__*/_interopDefaultLegacy(spaceTrim$1);
|
|
10
9
|
var hexEncoder__default = /*#__PURE__*/_interopDefaultLegacy(hexEncoder);
|
|
10
|
+
var spaceTrim__default = /*#__PURE__*/_interopDefaultLegacy(spaceTrim$1);
|
|
11
11
|
var sha256__default = /*#__PURE__*/_interopDefaultLegacy(sha256);
|
|
12
12
|
var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
|
|
13
13
|
var colors__default = /*#__PURE__*/_interopDefaultLegacy(colors);
|
|
@@ -28,131 +28,12 @@
|
|
|
28
28
|
* @generated
|
|
29
29
|
* @see https://github.com/webgptorg/promptbook
|
|
30
30
|
*/
|
|
31
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.103.0-
|
|
31
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.103.0-49';
|
|
32
32
|
/**
|
|
33
33
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
34
34
|
* Note: [š] Ignore a discrepancy between file name and entity name
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
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"}];
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Checks if value is valid email
|
|
41
|
-
*
|
|
42
|
-
* @public exported from `@promptbook/utils`
|
|
43
|
-
*/
|
|
44
|
-
function isValidEmail(email) {
|
|
45
|
-
if (typeof email !== 'string') {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
if (email.split('\n').length > 1) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
return /^.+@.+\..+$/.test(email);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Tests if given string is valid file path.
|
|
56
|
-
*
|
|
57
|
-
* Note: This does not check if the file exists only if the path is valid
|
|
58
|
-
* @public exported from `@promptbook/utils`
|
|
59
|
-
*/
|
|
60
|
-
function isValidFilePath(filename) {
|
|
61
|
-
if (typeof filename !== 'string') {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
if (filename.split('\n').length > 1) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
// Normalize slashes early so heuristics can detect path-like inputs
|
|
68
|
-
const filenameSlashes = filename.replace(/\\/g, '/');
|
|
69
|
-
// Reject strings that look like sentences (informational text)
|
|
70
|
-
// Heuristic: contains multiple spaces and ends with a period, or contains typical sentence punctuation
|
|
71
|
-
// But skip this heuristic if the string looks like a path (contains '/' or starts with a drive letter)
|
|
72
|
-
if (filename.trim().length > 60 && // long enough to be a sentence
|
|
73
|
-
/[.!?]/.test(filename) && // contains sentence punctuation
|
|
74
|
-
filename.split(' ').length > 8 && // has many words
|
|
75
|
-
!/\/|^[A-Z]:/i.test(filenameSlashes) // do NOT treat as sentence if looks like a path
|
|
76
|
-
) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
// Absolute Unix path: /hello.txt
|
|
80
|
-
if (/^(\/)/i.test(filenameSlashes)) {
|
|
81
|
-
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
// Absolute Windows path: C:/ or C:\ (allow spaces and multiple dots in filename)
|
|
85
|
-
if (/^[A-Z]:\/.+$/i.test(filenameSlashes)) {
|
|
86
|
-
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
// Relative path: ./hello.txt
|
|
90
|
-
if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
|
|
91
|
-
// console.log(filename, 'Relative path: ./hello.txt');
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
|
-
// Allow paths like foo/hello
|
|
95
|
-
if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
|
|
96
|
-
// console.log(filename, 'Allow paths like foo/hello');
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
// Allow paths like hello.book
|
|
100
|
-
if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
|
|
101
|
-
// console.log(filename, 'Allow paths like hello.book');
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* TODO: [š] Implement for MacOs
|
|
108
|
-
*/
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Tests if given string is valid URL.
|
|
112
|
-
*
|
|
113
|
-
* Note: [š] This function is idempotent.
|
|
114
|
-
* Note: Dataurl are considered perfectly valid.
|
|
115
|
-
* Note: There are two similar functions:
|
|
116
|
-
* - `isValidUrl` which tests any URL
|
|
117
|
-
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
118
|
-
*
|
|
119
|
-
* @public exported from `@promptbook/utils`
|
|
120
|
-
*/
|
|
121
|
-
function isValidUrl(url) {
|
|
122
|
-
if (typeof url !== 'string') {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
try {
|
|
126
|
-
if (url.startsWith('blob:')) {
|
|
127
|
-
url = url.replace(/^blob:/, '');
|
|
128
|
-
}
|
|
129
|
-
const urlObject = new URL(url /* because fail is handled */);
|
|
130
|
-
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
142
|
-
*
|
|
143
|
-
* @public exported from `@promptbook/core`
|
|
144
|
-
*/
|
|
145
|
-
class ParseError extends Error {
|
|
146
|
-
constructor(message) {
|
|
147
|
-
super(message);
|
|
148
|
-
this.name = 'ParseError';
|
|
149
|
-
Object.setPrototypeOf(this, ParseError.prototype);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
154
|
-
*/
|
|
155
|
-
|
|
156
37
|
/**
|
|
157
38
|
* Available remote servers for the Promptbook
|
|
158
39
|
*
|
|
@@ -186,6 +67,7 @@
|
|
|
186
67
|
*/
|
|
187
68
|
];
|
|
188
69
|
/**
|
|
70
|
+
* TODO: [š±āš] Auto-federated server from url in here
|
|
189
71
|
* Note: [š] Ignore a discrepancy between file name and entity name
|
|
190
72
|
*/
|
|
191
73
|
|
|
@@ -519,6 +401,9 @@
|
|
|
519
401
|
if (hex.length === 3) {
|
|
520
402
|
return Color.fromHex3(hex);
|
|
521
403
|
}
|
|
404
|
+
if (hex.length === 4) {
|
|
405
|
+
return Color.fromHex4(hex);
|
|
406
|
+
}
|
|
522
407
|
if (hex.length === 6) {
|
|
523
408
|
return Color.fromHex6(hex);
|
|
524
409
|
}
|
|
@@ -539,6 +424,19 @@
|
|
|
539
424
|
const b = parseInt(hex.substr(2, 1), 16) * 16;
|
|
540
425
|
return take(new Color(r, g, b));
|
|
541
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* Creates a new Color instance from color in hex format with 4 digits (with alpha channel)
|
|
429
|
+
*
|
|
430
|
+
* @param color in hex for example `09df`
|
|
431
|
+
* @returns Color object
|
|
432
|
+
*/
|
|
433
|
+
static fromHex4(hex) {
|
|
434
|
+
const r = parseInt(hex.substr(0, 1), 16) * 16;
|
|
435
|
+
const g = parseInt(hex.substr(1, 1), 16) * 16;
|
|
436
|
+
const b = parseInt(hex.substr(2, 1), 16) * 16;
|
|
437
|
+
const a = parseInt(hex.substr(3, 1), 16) * 16;
|
|
438
|
+
return take(new Color(r, g, b, a));
|
|
439
|
+
}
|
|
542
440
|
/**
|
|
543
441
|
* Creates a new Color instance from color in hex format with 6 color digits (without alpha channel)
|
|
544
442
|
*
|
|
@@ -729,7 +627,8 @@
|
|
|
729
627
|
* @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
|
|
730
628
|
*/
|
|
731
629
|
static isHexColorString(value) {
|
|
732
|
-
return typeof value === 'string' &&
|
|
630
|
+
return (typeof value === 'string' &&
|
|
631
|
+
/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
|
|
733
632
|
}
|
|
734
633
|
/**
|
|
735
634
|
* Creates new Color object
|
|
@@ -1070,6 +969,7 @@
|
|
|
1070
969
|
const PROMPTBOOK_SYNTAX_COLORS = {
|
|
1071
970
|
TITLE: Color.fromHex('#244EA8'),
|
|
1072
971
|
LINE: Color.fromHex('#eeeeee'),
|
|
972
|
+
SEPARATOR: Color.fromHex('#cccccc'),
|
|
1073
973
|
COMMITMENT: Color.fromHex('#DA0F78'),
|
|
1074
974
|
PARAMETER: Color.fromHex('#8e44ad'),
|
|
1075
975
|
};
|
|
@@ -1520,88 +1420,309 @@
|
|
|
1520
1420
|
}
|
|
1521
1421
|
|
|
1522
1422
|
/**
|
|
1523
|
-
*
|
|
1524
|
-
*
|
|
1525
|
-
* @param value The string to check
|
|
1526
|
-
* @returns `true` if the string is a valid JSON string, false otherwise
|
|
1423
|
+
* Format either small or big number
|
|
1527
1424
|
*
|
|
1528
1425
|
* @public exported from `@promptbook/utils`
|
|
1529
1426
|
*/
|
|
1530
|
-
function
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
return true;
|
|
1427
|
+
function numberToString(value) {
|
|
1428
|
+
if (value === 0) {
|
|
1429
|
+
return '0';
|
|
1534
1430
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1431
|
+
else if (Number.isNaN(value)) {
|
|
1432
|
+
return VALUE_STRINGS.nan;
|
|
1433
|
+
}
|
|
1434
|
+
else if (value === Infinity) {
|
|
1435
|
+
return VALUE_STRINGS.infinity;
|
|
1436
|
+
}
|
|
1437
|
+
else if (value === -Infinity) {
|
|
1438
|
+
return VALUE_STRINGS.negativeInfinity;
|
|
1439
|
+
}
|
|
1440
|
+
for (let exponent = 0; exponent < 15; exponent++) {
|
|
1441
|
+
const factor = 10 ** exponent;
|
|
1442
|
+
const valueRounded = Math.round(value * factor) / factor;
|
|
1443
|
+
if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
|
|
1444
|
+
return valueRounded.toFixed(exponent);
|
|
1539
1445
|
}
|
|
1540
|
-
return false;
|
|
1541
1446
|
}
|
|
1447
|
+
return value.toString();
|
|
1542
1448
|
}
|
|
1543
1449
|
|
|
1544
1450
|
/**
|
|
1545
|
-
* Function `
|
|
1546
|
-
*
|
|
1451
|
+
* Function `valueToString` will convert the given value to string
|
|
1452
|
+
* This is useful and used in the `templateParameters` function
|
|
1547
1453
|
*
|
|
1548
|
-
* Note:
|
|
1454
|
+
* Note: This function is not just calling `toString` method
|
|
1455
|
+
* It's more complex and can handle this conversion specifically for LLM models
|
|
1456
|
+
* See `VALUE_STRINGS`
|
|
1549
1457
|
*
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1458
|
+
* Note: There are 2 similar functions
|
|
1459
|
+
* - `valueToString` converts value to string for LLM models as human-readable string
|
|
1460
|
+
* - `asSerializable` converts value to string to preserve full information to be able to convert it back
|
|
1461
|
+
*
|
|
1462
|
+
* @public exported from `@promptbook/utils`
|
|
1554
1463
|
*/
|
|
1555
|
-
function
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1464
|
+
function valueToString(value) {
|
|
1465
|
+
try {
|
|
1466
|
+
if (value === '') {
|
|
1467
|
+
return VALUE_STRINGS.empty;
|
|
1468
|
+
}
|
|
1469
|
+
else if (value === null) {
|
|
1470
|
+
return VALUE_STRINGS.null;
|
|
1471
|
+
}
|
|
1472
|
+
else if (value === undefined) {
|
|
1473
|
+
return VALUE_STRINGS.undefined;
|
|
1474
|
+
}
|
|
1475
|
+
else if (typeof value === 'string') {
|
|
1476
|
+
return value;
|
|
1477
|
+
}
|
|
1478
|
+
else if (typeof value === 'number') {
|
|
1479
|
+
return numberToString(value);
|
|
1480
|
+
}
|
|
1481
|
+
else if (value instanceof Date) {
|
|
1482
|
+
return value.toISOString();
|
|
1483
|
+
}
|
|
1484
|
+
else {
|
|
1485
|
+
try {
|
|
1486
|
+
return JSON.stringify(value);
|
|
1487
|
+
}
|
|
1488
|
+
catch (error) {
|
|
1489
|
+
if (error instanceof TypeError && error.message.includes('circular structure')) {
|
|
1490
|
+
return VALUE_STRINGS.circular;
|
|
1491
|
+
}
|
|
1492
|
+
throw error;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1564
1495
|
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1496
|
+
catch (error) {
|
|
1497
|
+
assertsError(error);
|
|
1498
|
+
console.error(error);
|
|
1499
|
+
return VALUE_STRINGS.unserializable;
|
|
1567
1500
|
}
|
|
1568
|
-
// <- TODO: Implement the validation + add tests when the pipeline logic considered as invalid
|
|
1569
|
-
return pipelineString;
|
|
1570
1501
|
}
|
|
1571
|
-
/**
|
|
1572
|
-
* TODO: [š§ ][š“] Where is the best location for this file
|
|
1573
|
-
*/
|
|
1574
1502
|
|
|
1575
1503
|
/**
|
|
1576
|
-
*
|
|
1504
|
+
* Computes SHA-256 hash of the given object
|
|
1577
1505
|
*
|
|
1578
|
-
* @
|
|
1579
|
-
* @returns formatted html code
|
|
1580
|
-
* @private withing the package because of HUGE size of prettier dependency
|
|
1581
|
-
* @deprecated Prettier removed from Promptbook due to package size
|
|
1506
|
+
* @public exported from `@promptbook/utils`
|
|
1582
1507
|
*/
|
|
1583
|
-
function
|
|
1584
|
-
return (
|
|
1508
|
+
function computeHash(value) {
|
|
1509
|
+
return cryptoJs.SHA256(hexEncoder__default["default"].parse(spaceTrim__default["default"](valueToString(value)))).toString( /* hex */);
|
|
1585
1510
|
}
|
|
1511
|
+
/**
|
|
1512
|
+
* TODO: [š„¬][š„¬] Use this ACRY
|
|
1513
|
+
*/
|
|
1586
1514
|
|
|
1587
1515
|
/**
|
|
1588
|
-
*
|
|
1589
|
-
*
|
|
1590
|
-
* Note: [š] This function is idempotent.
|
|
1516
|
+
* Computes SHA-256 hash of the agent source
|
|
1591
1517
|
*
|
|
1592
|
-
* @public exported from `@promptbook/
|
|
1518
|
+
* @public exported from `@promptbook/core`
|
|
1593
1519
|
*/
|
|
1594
|
-
function
|
|
1595
|
-
return
|
|
1520
|
+
function computeAgentHash(agentSource) {
|
|
1521
|
+
return computeHash(agentSource);
|
|
1596
1522
|
}
|
|
1597
1523
|
|
|
1524
|
+
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"}];
|
|
1525
|
+
|
|
1598
1526
|
/**
|
|
1599
|
-
*
|
|
1527
|
+
* Checks if value is valid email
|
|
1600
1528
|
*
|
|
1601
|
-
* @
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1529
|
+
* @public exported from `@promptbook/utils`
|
|
1530
|
+
*/
|
|
1531
|
+
function isValidEmail(email) {
|
|
1532
|
+
if (typeof email !== 'string') {
|
|
1533
|
+
return false;
|
|
1534
|
+
}
|
|
1535
|
+
if (email.split('\n').length > 1) {
|
|
1536
|
+
return false;
|
|
1537
|
+
}
|
|
1538
|
+
return /^.+@.+\..+$/.test(email);
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* Tests if given string is valid file path.
|
|
1543
|
+
*
|
|
1544
|
+
* Note: This does not check if the file exists only if the path is valid
|
|
1545
|
+
* @public exported from `@promptbook/utils`
|
|
1546
|
+
*/
|
|
1547
|
+
function isValidFilePath(filename) {
|
|
1548
|
+
if (typeof filename !== 'string') {
|
|
1549
|
+
return false;
|
|
1550
|
+
}
|
|
1551
|
+
if (filename.split('\n').length > 1) {
|
|
1552
|
+
return false;
|
|
1553
|
+
}
|
|
1554
|
+
// Normalize slashes early so heuristics can detect path-like inputs
|
|
1555
|
+
const filenameSlashes = filename.replace(/\\/g, '/');
|
|
1556
|
+
// Reject strings that look like sentences (informational text)
|
|
1557
|
+
// Heuristic: contains multiple spaces and ends with a period, or contains typical sentence punctuation
|
|
1558
|
+
// But skip this heuristic if the string looks like a path (contains '/' or starts with a drive letter)
|
|
1559
|
+
if (filename.trim().length > 60 && // long enough to be a sentence
|
|
1560
|
+
/[.!?]/.test(filename) && // contains sentence punctuation
|
|
1561
|
+
filename.split(' ').length > 8 && // has many words
|
|
1562
|
+
!/\/|^[A-Z]:/i.test(filenameSlashes) // do NOT treat as sentence if looks like a path
|
|
1563
|
+
) {
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
// Absolute Unix path: /hello.txt
|
|
1567
|
+
if (/^(\/)/i.test(filenameSlashes)) {
|
|
1568
|
+
// console.log(filename, 'Absolute Unix path: /hello.txt');
|
|
1569
|
+
return true;
|
|
1570
|
+
}
|
|
1571
|
+
// Absolute Windows path: C:/ or C:\ (allow spaces and multiple dots in filename)
|
|
1572
|
+
if (/^[A-Z]:\/.+$/i.test(filenameSlashes)) {
|
|
1573
|
+
// console.log(filename, 'Absolute Windows path: /hello.txt');
|
|
1574
|
+
return true;
|
|
1575
|
+
}
|
|
1576
|
+
// Relative path: ./hello.txt
|
|
1577
|
+
if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
|
|
1578
|
+
// console.log(filename, 'Relative path: ./hello.txt');
|
|
1579
|
+
return true;
|
|
1580
|
+
}
|
|
1581
|
+
// Allow paths like foo/hello
|
|
1582
|
+
if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
|
|
1583
|
+
// console.log(filename, 'Allow paths like foo/hello');
|
|
1584
|
+
return true;
|
|
1585
|
+
}
|
|
1586
|
+
// Allow paths like hello.book
|
|
1587
|
+
if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
|
|
1588
|
+
// console.log(filename, 'Allow paths like hello.book');
|
|
1589
|
+
return true;
|
|
1590
|
+
}
|
|
1591
|
+
return false;
|
|
1592
|
+
}
|
|
1593
|
+
/**
|
|
1594
|
+
* TODO: [š] Implement for MacOs
|
|
1595
|
+
*/
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* Tests if given string is valid URL.
|
|
1599
|
+
*
|
|
1600
|
+
* Note: [š] This function is idempotent.
|
|
1601
|
+
* Note: Dataurl are considered perfectly valid.
|
|
1602
|
+
* Note: There are two similar functions:
|
|
1603
|
+
* - `isValidUrl` which tests any URL
|
|
1604
|
+
* - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
|
|
1605
|
+
*
|
|
1606
|
+
* @public exported from `@promptbook/utils`
|
|
1607
|
+
*/
|
|
1608
|
+
function isValidUrl(url) {
|
|
1609
|
+
if (typeof url !== 'string') {
|
|
1610
|
+
return false;
|
|
1611
|
+
}
|
|
1612
|
+
try {
|
|
1613
|
+
if (url.startsWith('blob:')) {
|
|
1614
|
+
url = url.replace(/^blob:/, '');
|
|
1615
|
+
}
|
|
1616
|
+
const urlObject = new URL(url /* because fail is handled */);
|
|
1617
|
+
if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
return true;
|
|
1621
|
+
}
|
|
1622
|
+
catch (error) {
|
|
1623
|
+
return false;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
|
|
1629
|
+
*
|
|
1630
|
+
* @public exported from `@promptbook/core`
|
|
1631
|
+
*/
|
|
1632
|
+
class ParseError extends Error {
|
|
1633
|
+
constructor(message) {
|
|
1634
|
+
super(message);
|
|
1635
|
+
this.name = 'ParseError';
|
|
1636
|
+
Object.setPrototypeOf(this, ParseError.prototype);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
/**
|
|
1640
|
+
* TODO: Maybe split `ParseError` and `ApplyError`
|
|
1641
|
+
*/
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* Function isValidJsonString will tell you if the string is valid JSON or not
|
|
1645
|
+
*
|
|
1646
|
+
* @param value The string to check
|
|
1647
|
+
* @returns `true` if the string is a valid JSON string, false otherwise
|
|
1648
|
+
*
|
|
1649
|
+
* @public exported from `@promptbook/utils`
|
|
1650
|
+
*/
|
|
1651
|
+
function isValidJsonString(value /* <- [šØāāļø] */) {
|
|
1652
|
+
try {
|
|
1653
|
+
JSON.parse(value);
|
|
1654
|
+
return true;
|
|
1655
|
+
}
|
|
1656
|
+
catch (error) {
|
|
1657
|
+
assertsError(error);
|
|
1658
|
+
if (error.message.includes('Unexpected token')) {
|
|
1659
|
+
return false;
|
|
1660
|
+
}
|
|
1661
|
+
return false;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
/**
|
|
1666
|
+
* Function `validatePipelineString` will validate the if the string is a valid pipeline string
|
|
1667
|
+
* It does not check if the string is fully logically correct, but if it is a string that can be a pipeline string or the string looks completely different.
|
|
1668
|
+
*
|
|
1669
|
+
* Note: [š] This function is idempotent.
|
|
1670
|
+
*
|
|
1671
|
+
* @param {string} pipelineString the candidate for a pipeline string
|
|
1672
|
+
* @returns {PipelineString} the same string as input, but validated as valid
|
|
1673
|
+
* @throws {ParseError} if the string is not a valid pipeline string
|
|
1674
|
+
* @public exported from `@promptbook/core`
|
|
1675
|
+
*/
|
|
1676
|
+
function validatePipelineString(pipelineString) {
|
|
1677
|
+
if (isValidJsonString(pipelineString)) {
|
|
1678
|
+
throw new ParseError('Expected a book, but got a JSON string');
|
|
1679
|
+
}
|
|
1680
|
+
else if (isValidUrl(pipelineString)) {
|
|
1681
|
+
throw new ParseError(`Expected a book, but got just the URL "${pipelineString}"`);
|
|
1682
|
+
}
|
|
1683
|
+
else if (isValidFilePath(pipelineString)) {
|
|
1684
|
+
throw new ParseError(`Expected a book, but got just the file path "${pipelineString}"`);
|
|
1685
|
+
}
|
|
1686
|
+
else if (isValidEmail(pipelineString)) {
|
|
1687
|
+
throw new ParseError(`Expected a book, but got just the email "${pipelineString}"`);
|
|
1688
|
+
}
|
|
1689
|
+
// <- TODO: Implement the validation + add tests when the pipeline logic considered as invalid
|
|
1690
|
+
return pipelineString;
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
* TODO: [š§ ][š“] Where is the best location for this file
|
|
1694
|
+
*/
|
|
1695
|
+
|
|
1696
|
+
/**
|
|
1697
|
+
* Prettify the html code
|
|
1698
|
+
*
|
|
1699
|
+
* @param content raw html code
|
|
1700
|
+
* @returns formatted html code
|
|
1701
|
+
* @private withing the package because of HUGE size of prettier dependency
|
|
1702
|
+
* @deprecated Prettier removed from Promptbook due to package size
|
|
1703
|
+
*/
|
|
1704
|
+
function prettifyMarkdown(content) {
|
|
1705
|
+
return (content + `\n\n<!-- Note: Prettier removed from Promptbook -->`);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
/**
|
|
1709
|
+
* Makes first letter of a string uppercase
|
|
1710
|
+
*
|
|
1711
|
+
* Note: [š] This function is idempotent.
|
|
1712
|
+
*
|
|
1713
|
+
* @public exported from `@promptbook/utils`
|
|
1714
|
+
*/
|
|
1715
|
+
function capitalize(word) {
|
|
1716
|
+
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
/**
|
|
1720
|
+
* Converts promptbook in JSON format to string format
|
|
1721
|
+
*
|
|
1722
|
+
* @deprecated TODO: [š„][š§ ] Backup original files in `PipelineJson` same as in Promptbook.studio
|
|
1723
|
+
* @param pipelineJson Promptbook in JSON format (.bookc)
|
|
1724
|
+
* @returns Promptbook in string format (.book.md)
|
|
1725
|
+
* @public exported from `@promptbook/core`
|
|
1605
1726
|
*/
|
|
1606
1727
|
function pipelineJsonToString(pipelineJson) {
|
|
1607
1728
|
const { title, pipelineUrl, bookVersion, description, parameters, tasks } = pipelineJson;
|
|
@@ -1931,7 +2052,7 @@
|
|
|
1931
2052
|
TODO: [š§ ] Is there a better implementation?
|
|
1932
2053
|
> const propertyNames = Object.getOwnPropertyNames(objectValue);
|
|
1933
2054
|
> for (const propertyName of propertyNames) {
|
|
1934
|
-
> const value = (objectValue as
|
|
2055
|
+
> const value = (objectValue as chococake)[propertyName];
|
|
1935
2056
|
> if (value && typeof value === 'object') {
|
|
1936
2057
|
> deepClone(value);
|
|
1937
2058
|
> }
|
|
@@ -2784,7 +2905,7 @@
|
|
|
2784
2905
|
}
|
|
2785
2906
|
}
|
|
2786
2907
|
/**
|
|
2787
|
-
* TODO:
|
|
2908
|
+
* TODO: [š±āš] Explain that NotFoundError ([š±āš] and other specific errors) has priority over DatabaseError in some contexts
|
|
2788
2909
|
*/
|
|
2789
2910
|
|
|
2790
2911
|
/**
|
|
@@ -4326,6 +4447,8 @@
|
|
|
4326
4447
|
/**
|
|
4327
4448
|
* Converts a given text to kebab-case format.
|
|
4328
4449
|
*
|
|
4450
|
+
* Note: [š] This function is idempotent.
|
|
4451
|
+
*
|
|
4329
4452
|
* @param text The text to be converted.
|
|
4330
4453
|
* @returns The kebab-case formatted string.
|
|
4331
4454
|
* @example 'hello-world'
|
|
@@ -4481,6 +4604,8 @@
|
|
|
4481
4604
|
/**
|
|
4482
4605
|
* Converts a title string into a normalized name.
|
|
4483
4606
|
*
|
|
4607
|
+
* Note: [š] This function is idempotent.
|
|
4608
|
+
*
|
|
4484
4609
|
* @param value The title string to be converted to a name.
|
|
4485
4610
|
* @returns A normalized name derived from the input title.
|
|
4486
4611
|
* @example 'Hello World!' -> 'hello-world'
|
|
@@ -5050,98 +5175,17 @@
|
|
|
5050
5175
|
*/
|
|
5051
5176
|
|
|
5052
5177
|
/**
|
|
5053
|
-
*
|
|
5054
|
-
*
|
|
5055
|
-
* @public exported from `@promptbook/utils`
|
|
5056
|
-
*/
|
|
5057
|
-
function numberToString(value) {
|
|
5058
|
-
if (value === 0) {
|
|
5059
|
-
return '0';
|
|
5060
|
-
}
|
|
5061
|
-
else if (Number.isNaN(value)) {
|
|
5062
|
-
return VALUE_STRINGS.nan;
|
|
5063
|
-
}
|
|
5064
|
-
else if (value === Infinity) {
|
|
5065
|
-
return VALUE_STRINGS.infinity;
|
|
5066
|
-
}
|
|
5067
|
-
else if (value === -Infinity) {
|
|
5068
|
-
return VALUE_STRINGS.negativeInfinity;
|
|
5069
|
-
}
|
|
5070
|
-
for (let exponent = 0; exponent < 15; exponent++) {
|
|
5071
|
-
const factor = 10 ** exponent;
|
|
5072
|
-
const valueRounded = Math.round(value * factor) / factor;
|
|
5073
|
-
if (Math.abs(value - valueRounded) / value < SMALL_NUMBER) {
|
|
5074
|
-
return valueRounded.toFixed(exponent);
|
|
5075
|
-
}
|
|
5076
|
-
}
|
|
5077
|
-
return value.toString();
|
|
5078
|
-
}
|
|
5079
|
-
|
|
5080
|
-
/**
|
|
5081
|
-
* Function `valueToString` will convert the given value to string
|
|
5082
|
-
* This is useful and used in the `templateParameters` function
|
|
5083
|
-
*
|
|
5084
|
-
* Note: This function is not just calling `toString` method
|
|
5085
|
-
* It's more complex and can handle this conversion specifically for LLM models
|
|
5086
|
-
* See `VALUE_STRINGS`
|
|
5087
|
-
*
|
|
5088
|
-
* Note: There are 2 similar functions
|
|
5089
|
-
* - `valueToString` converts value to string for LLM models as human-readable string
|
|
5090
|
-
* - `asSerializable` converts value to string to preserve full information to be able to convert it back
|
|
5178
|
+
* Parses the given script and returns the list of all used variables that are not defined in the script
|
|
5091
5179
|
*
|
|
5092
|
-
* @
|
|
5180
|
+
* @param script from which to extract the variables
|
|
5181
|
+
* @returns the list of variable names
|
|
5182
|
+
* @throws {ParseError} if the script is invalid
|
|
5183
|
+
* @public exported from `@promptbook/javascript`
|
|
5093
5184
|
*/
|
|
5094
|
-
function
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
}
|
|
5099
|
-
else if (value === null) {
|
|
5100
|
-
return VALUE_STRINGS.null;
|
|
5101
|
-
}
|
|
5102
|
-
else if (value === undefined) {
|
|
5103
|
-
return VALUE_STRINGS.undefined;
|
|
5104
|
-
}
|
|
5105
|
-
else if (typeof value === 'string') {
|
|
5106
|
-
return value;
|
|
5107
|
-
}
|
|
5108
|
-
else if (typeof value === 'number') {
|
|
5109
|
-
return numberToString(value);
|
|
5110
|
-
}
|
|
5111
|
-
else if (value instanceof Date) {
|
|
5112
|
-
return value.toISOString();
|
|
5113
|
-
}
|
|
5114
|
-
else {
|
|
5115
|
-
try {
|
|
5116
|
-
return JSON.stringify(value);
|
|
5117
|
-
}
|
|
5118
|
-
catch (error) {
|
|
5119
|
-
if (error instanceof TypeError && error.message.includes('circular structure')) {
|
|
5120
|
-
return VALUE_STRINGS.circular;
|
|
5121
|
-
}
|
|
5122
|
-
throw error;
|
|
5123
|
-
}
|
|
5124
|
-
}
|
|
5125
|
-
}
|
|
5126
|
-
catch (error) {
|
|
5127
|
-
assertsError(error);
|
|
5128
|
-
console.error(error);
|
|
5129
|
-
return VALUE_STRINGS.unserializable;
|
|
5130
|
-
}
|
|
5131
|
-
}
|
|
5132
|
-
|
|
5133
|
-
/**
|
|
5134
|
-
* Parses the given script and returns the list of all used variables that are not defined in the script
|
|
5135
|
-
*
|
|
5136
|
-
* @param script from which to extract the variables
|
|
5137
|
-
* @returns the list of variable names
|
|
5138
|
-
* @throws {ParseError} if the script is invalid
|
|
5139
|
-
* @public exported from `@promptbook/javascript`
|
|
5140
|
-
*/
|
|
5141
|
-
function extractVariablesFromJavascript(script) {
|
|
5142
|
-
const variables = new Set();
|
|
5143
|
-
const originalScript = script;
|
|
5144
|
-
script = `(()=>{${script}})()`;
|
|
5185
|
+
function extractVariablesFromJavascript(script) {
|
|
5186
|
+
const variables = new Set();
|
|
5187
|
+
const originalScript = script;
|
|
5188
|
+
script = `(()=>{${script}})()`;
|
|
5145
5189
|
try {
|
|
5146
5190
|
for (let i = 0; i < LOOP_LIMIT; i++)
|
|
5147
5191
|
try {
|
|
@@ -8056,6 +8100,60 @@
|
|
|
8056
8100
|
* Note: [š] Ignore a discrepancy between file name and entity name
|
|
8057
8101
|
*/
|
|
8058
8102
|
|
|
8103
|
+
/**
|
|
8104
|
+
* INITIAL MESSAGE commitment definition
|
|
8105
|
+
*
|
|
8106
|
+
* The INITIAL MESSAGE commitment defines the first message that the user sees when opening the chat.
|
|
8107
|
+
* It is used to greet the user and set the tone of the conversation.
|
|
8108
|
+
*
|
|
8109
|
+
* Example usage in agent source:
|
|
8110
|
+
*
|
|
8111
|
+
* ```book
|
|
8112
|
+
* INITIAL MESSAGE Hello! I am ready to help you with your tasks.
|
|
8113
|
+
* ```
|
|
8114
|
+
*
|
|
8115
|
+
* @private [šŖ] Maybe export the commitments through some package
|
|
8116
|
+
*/
|
|
8117
|
+
class InitialMessageCommitmentDefinition extends BaseCommitmentDefinition {
|
|
8118
|
+
constructor() {
|
|
8119
|
+
super('INITIAL MESSAGE');
|
|
8120
|
+
}
|
|
8121
|
+
/**
|
|
8122
|
+
* Short one-line description of INITIAL MESSAGE.
|
|
8123
|
+
*/
|
|
8124
|
+
get description() {
|
|
8125
|
+
return 'Defines the **initial message** shown to the user when the chat starts.';
|
|
8126
|
+
}
|
|
8127
|
+
/**
|
|
8128
|
+
* Markdown documentation for INITIAL MESSAGE commitment.
|
|
8129
|
+
*/
|
|
8130
|
+
get documentation() {
|
|
8131
|
+
return spaceTrim$1.spaceTrim(`
|
|
8132
|
+
# ${this.type}
|
|
8133
|
+
|
|
8134
|
+
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).
|
|
8135
|
+
|
|
8136
|
+
## Key aspects
|
|
8137
|
+
|
|
8138
|
+
- Used to greet the user.
|
|
8139
|
+
- Sets the tone of the conversation.
|
|
8140
|
+
- Displayed immediately when the chat interface loads.
|
|
8141
|
+
|
|
8142
|
+
## Examples
|
|
8143
|
+
|
|
8144
|
+
\`\`\`book
|
|
8145
|
+
Support Agent
|
|
8146
|
+
|
|
8147
|
+
PERSONA You are a helpful support agent.
|
|
8148
|
+
INITIAL MESSAGE Hi there! How can I assist you today?
|
|
8149
|
+
\`\`\`
|
|
8150
|
+
`);
|
|
8151
|
+
}
|
|
8152
|
+
applyToAgentModelRequirements(requirements, content) {
|
|
8153
|
+
return requirements;
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
|
|
8059
8157
|
/**
|
|
8060
8158
|
* MESSAGE commitment definition
|
|
8061
8159
|
*
|
|
@@ -8760,6 +8858,7 @@
|
|
|
8760
8858
|
// Keep everything after the PERSONA section
|
|
8761
8859
|
cleanedMessage = lines.slice(personaEndIndex).join('\n').trim();
|
|
8762
8860
|
}
|
|
8861
|
+
// TODO: [š] There should be `agentFullname` not `agentName`
|
|
8763
8862
|
// Create new system message with persona at the beginning
|
|
8764
8863
|
// Format: "You are {agentName}\n{personaContent}"
|
|
8765
8864
|
// The # PERSONA comment will be removed later by removeCommentsFromSystemMessage
|
|
@@ -9216,6 +9315,7 @@
|
|
|
9216
9315
|
new NoteCommitmentDefinition('NONCE'),
|
|
9217
9316
|
new GoalCommitmentDefinition('GOAL'),
|
|
9218
9317
|
new GoalCommitmentDefinition('GOALS'),
|
|
9318
|
+
new InitialMessageCommitmentDefinition(),
|
|
9219
9319
|
new MessageCommitmentDefinition('MESSAGE'),
|
|
9220
9320
|
new MessageCommitmentDefinition('MESSAGES'),
|
|
9221
9321
|
new ScenarioCommitmentDefinition('SCENARIO'),
|
|
@@ -9581,6 +9681,8 @@
|
|
|
9581
9681
|
/**
|
|
9582
9682
|
* Normalizes a given text to camelCase format.
|
|
9583
9683
|
*
|
|
9684
|
+
* Note: [š] This function is idempotent.
|
|
9685
|
+
*
|
|
9584
9686
|
* @param text The text to be normalized.
|
|
9585
9687
|
* @param _isFirstLetterCapital Whether the first letter should be capitalized.
|
|
9586
9688
|
* @returns The camelCase formatted string.
|
|
@@ -9669,89 +9771,520 @@
|
|
|
9669
9771
|
*/
|
|
9670
9772
|
|
|
9671
9773
|
/**
|
|
9672
|
-
*
|
|
9774
|
+
* Creates a Mermaid graph based on the promptbook
|
|
9673
9775
|
*
|
|
9674
|
-
*
|
|
9675
|
-
* - `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.
|
|
9676
|
-
* - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronously.
|
|
9776
|
+
* Note: The result is not wrapped in a Markdown code block
|
|
9677
9777
|
*
|
|
9678
|
-
* @public exported from `@promptbook/
|
|
9778
|
+
* @public exported from `@promptbook/utils`
|
|
9679
9779
|
*/
|
|
9680
|
-
function
|
|
9681
|
-
const
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9780
|
+
function renderPromptbookMermaid(pipelineJson, options) {
|
|
9781
|
+
const { linkTask = () => null } = options || {};
|
|
9782
|
+
const MERMAID_PREFIX = 'pipeline_';
|
|
9783
|
+
const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
|
|
9784
|
+
const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
|
|
9785
|
+
const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
|
|
9786
|
+
const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
|
|
9787
|
+
const parameterNameToTaskName = (parameterName) => {
|
|
9788
|
+
if (parameterName === 'knowledge') {
|
|
9789
|
+
return MERMAID_KNOWLEDGE_NAME;
|
|
9687
9790
|
}
|
|
9688
|
-
if (
|
|
9689
|
-
|
|
9791
|
+
else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
9792
|
+
return MERMAID_RESERVED_NAME;
|
|
9690
9793
|
}
|
|
9691
|
-
|
|
9692
|
-
|
|
9794
|
+
const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
|
|
9795
|
+
if (!parameter) {
|
|
9796
|
+
throw new UnexpectedError(`Could not find {${parameterName}}`);
|
|
9797
|
+
// <- TODO: This causes problems when {knowledge} and other reserved parameters are used
|
|
9693
9798
|
}
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
const meta = {};
|
|
9697
|
-
for (const commitment of parseResult.commitments) {
|
|
9698
|
-
if (commitment.type !== 'META') {
|
|
9699
|
-
continue;
|
|
9799
|
+
if (parameter.isInput) {
|
|
9800
|
+
return MERMAID_INPUT_NAME;
|
|
9700
9801
|
}
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
// Generate gravatar fallback if no meta image specified
|
|
9707
|
-
if (!meta.image) {
|
|
9708
|
-
meta.image = generatePlaceholderAgentProfileImageUrl(parseResult.agentName || '!!');
|
|
9709
|
-
}
|
|
9710
|
-
// Parse parameters using unified approach - both @Parameter and {parameter} notations
|
|
9711
|
-
// are treated as the same syntax feature with unified representation
|
|
9712
|
-
const parameters = parseParameters(agentSource);
|
|
9713
|
-
return {
|
|
9714
|
-
agentName: parseResult.agentName,
|
|
9715
|
-
personaDescription,
|
|
9716
|
-
meta,
|
|
9717
|
-
parameters,
|
|
9802
|
+
const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
|
|
9803
|
+
if (!task) {
|
|
9804
|
+
throw new Error(`Could not find task for {${parameterName}}`);
|
|
9805
|
+
}
|
|
9806
|
+
return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
|
|
9718
9807
|
};
|
|
9808
|
+
const inputAndIntermediateParametersMermaid = pipelineJson.tasks
|
|
9809
|
+
.flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
|
|
9810
|
+
`${parameterNameToTaskName(resultingParameterName)}("${title}")`,
|
|
9811
|
+
...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
|
|
9812
|
+
])
|
|
9813
|
+
.join('\n');
|
|
9814
|
+
const outputParametersMermaid = pipelineJson.parameters
|
|
9815
|
+
.filter(({ isOutput }) => isOutput)
|
|
9816
|
+
.map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
|
|
9817
|
+
.join('\n');
|
|
9818
|
+
const linksMermaid = pipelineJson.tasks
|
|
9819
|
+
.map((task) => {
|
|
9820
|
+
const link = linkTask(task);
|
|
9821
|
+
if (link === null) {
|
|
9822
|
+
return '';
|
|
9823
|
+
}
|
|
9824
|
+
const { href, title } = link;
|
|
9825
|
+
const taskName = parameterNameToTaskName(task.resultingParameterName);
|
|
9826
|
+
return `click ${taskName} href "${href}" "${title}";`;
|
|
9827
|
+
})
|
|
9828
|
+
.filter((line) => line !== '')
|
|
9829
|
+
.join('\n');
|
|
9830
|
+
const interactionPointsMermaid = Object.entries({
|
|
9831
|
+
[MERMAID_INPUT_NAME]: 'Input',
|
|
9832
|
+
[MERMAID_OUTPUT_NAME]: 'Output',
|
|
9833
|
+
[MERMAID_RESERVED_NAME]: 'Other',
|
|
9834
|
+
[MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
|
|
9835
|
+
})
|
|
9836
|
+
.filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
|
|
9837
|
+
.map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
|
|
9838
|
+
.join('\n');
|
|
9839
|
+
const promptbookMermaid = spaceTrim$1.spaceTrim((block) => `
|
|
9840
|
+
|
|
9841
|
+
%% š® Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
|
|
9842
|
+
|
|
9843
|
+
flowchart LR
|
|
9844
|
+
subgraph "${pipelineJson.title}"
|
|
9845
|
+
|
|
9846
|
+
%% Basic configuration
|
|
9847
|
+
direction TB
|
|
9848
|
+
|
|
9849
|
+
%% Interaction points from pipeline to outside
|
|
9850
|
+
${block(interactionPointsMermaid)}
|
|
9851
|
+
|
|
9852
|
+
%% Input and intermediate parameters
|
|
9853
|
+
${block(inputAndIntermediateParametersMermaid)}
|
|
9854
|
+
|
|
9855
|
+
|
|
9856
|
+
%% Output parameters
|
|
9857
|
+
${block(outputParametersMermaid)}
|
|
9858
|
+
|
|
9859
|
+
%% Links
|
|
9860
|
+
${block(linksMermaid)}
|
|
9861
|
+
|
|
9862
|
+
%% Styles
|
|
9863
|
+
classDef ${MERMAID_INPUT_NAME} color: grey;
|
|
9864
|
+
classDef ${MERMAID_OUTPUT_NAME} color: grey;
|
|
9865
|
+
classDef ${MERMAID_RESERVED_NAME} color: grey;
|
|
9866
|
+
classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
|
|
9867
|
+
|
|
9868
|
+
end;
|
|
9869
|
+
|
|
9870
|
+
`);
|
|
9871
|
+
return promptbookMermaid;
|
|
9719
9872
|
}
|
|
9720
9873
|
/**
|
|
9721
|
-
* TODO: [
|
|
9874
|
+
* TODO: [š§ ] FOREACH in mermaid graph
|
|
9875
|
+
* TODO: [š§ ] Knowledge in mermaid graph
|
|
9876
|
+
* TODO: [š§ ] Personas in mermaid graph
|
|
9877
|
+
* TODO: Maybe use some Mermaid package instead of string templating
|
|
9878
|
+
* TODO: [š] When more than 2 functionalities, split into separate functions
|
|
9722
9879
|
*/
|
|
9723
9880
|
|
|
9724
9881
|
/**
|
|
9725
|
-
*
|
|
9882
|
+
* Tag function for notating a prompt as template literal
|
|
9726
9883
|
*
|
|
9727
|
-
* There are
|
|
9728
|
-
*
|
|
9729
|
-
*
|
|
9884
|
+
* Note: There are 3 similar functions:
|
|
9885
|
+
* 1) `prompt` for notating single prompt exported from `@promptbook/utils`
|
|
9886
|
+
* 2) `promptTemplate` alias for `prompt`
|
|
9887
|
+
* 3) `book` for notating and validating entire books exported from `@promptbook/utils`
|
|
9730
9888
|
*
|
|
9731
|
-
* @
|
|
9889
|
+
* @param strings
|
|
9890
|
+
* @param values
|
|
9891
|
+
* @returns the prompt string
|
|
9892
|
+
* @public exported from `@promptbook/utils`
|
|
9732
9893
|
*/
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
if (availableModels && !modelName && llmTools) {
|
|
9737
|
-
const selectedModelName = await selectBestModelUsingPersona(agentSource, llmTools);
|
|
9738
|
-
return createAgentModelRequirementsWithCommitments(agentSource, selectedModelName);
|
|
9894
|
+
function prompt(strings, ...values) {
|
|
9895
|
+
if (values.length === 0) {
|
|
9896
|
+
return spaceTrim__default["default"](strings.join(''));
|
|
9739
9897
|
}
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9898
|
+
const stringsWithHiddenParameters = strings.map((stringsItem) =>
|
|
9899
|
+
// TODO: [0] DRY
|
|
9900
|
+
stringsItem.split('{').join(`${REPLACING_NONCE}beginbracket`).split('}').join(`${REPLACING_NONCE}endbracket`));
|
|
9901
|
+
const placeholderParameterNames = values.map((value, i) => `${REPLACING_NONCE}${i}`);
|
|
9902
|
+
const parameters = Object.fromEntries(values.map((value, i) => [placeholderParameterNames[i], value]));
|
|
9903
|
+
// Combine strings and values
|
|
9904
|
+
let pipelineString = stringsWithHiddenParameters.reduce((result, stringsItem, i) => placeholderParameterNames[i] === undefined
|
|
9905
|
+
? `${result}${stringsItem}`
|
|
9906
|
+
: `${result}${stringsItem}{${placeholderParameterNames[i]}}`, '');
|
|
9907
|
+
pipelineString = spaceTrim__default["default"](pipelineString);
|
|
9908
|
+
try {
|
|
9909
|
+
pipelineString = templateParameters(pipelineString, parameters);
|
|
9910
|
+
}
|
|
9911
|
+
catch (error) {
|
|
9912
|
+
if (!(error instanceof PipelineExecutionError)) {
|
|
9913
|
+
throw error;
|
|
9914
|
+
}
|
|
9915
|
+
console.error({ pipelineString, parameters, placeholderParameterNames, error });
|
|
9916
|
+
throw new UnexpectedError(spaceTrim__default["default"]((block) => `
|
|
9917
|
+
Internal error in prompt template literal
|
|
9918
|
+
|
|
9919
|
+
${block(JSON.stringify({ strings, values }, null, 4))}}
|
|
9920
|
+
|
|
9921
|
+
`));
|
|
9922
|
+
}
|
|
9923
|
+
// TODO: [0] DRY
|
|
9924
|
+
pipelineString = pipelineString
|
|
9925
|
+
.split(`${REPLACING_NONCE}beginbracket`)
|
|
9926
|
+
.join('{')
|
|
9927
|
+
.split(`${REPLACING_NONCE}endbracket`)
|
|
9928
|
+
.join('}');
|
|
9929
|
+
return pipelineString;
|
|
9930
|
+
}
|
|
9931
|
+
/**
|
|
9932
|
+
* TODO: [š§ ][š“] Where is the best location for this file
|
|
9933
|
+
* Note: [š] Ignore a discrepancy between file name and entity name
|
|
9934
|
+
*/
|
|
9935
|
+
|
|
9936
|
+
/**
|
|
9937
|
+
* Detects if the code is running in a browser environment in main thread (Not in a web worker)
|
|
9938
|
+
*
|
|
9939
|
+
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
9940
|
+
*
|
|
9941
|
+
* @public exported from `@promptbook/utils`
|
|
9942
|
+
*/
|
|
9943
|
+
const $isRunningInBrowser = new Function(`
|
|
9944
|
+
try {
|
|
9945
|
+
return this === window;
|
|
9946
|
+
} catch (e) {
|
|
9947
|
+
return false;
|
|
9948
|
+
}
|
|
9949
|
+
`);
|
|
9950
|
+
/**
|
|
9951
|
+
* TODO: [šŗ]
|
|
9952
|
+
*/
|
|
9953
|
+
|
|
9954
|
+
/**
|
|
9955
|
+
* Detects if the code is running in jest environment
|
|
9956
|
+
*
|
|
9957
|
+
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
9958
|
+
*
|
|
9959
|
+
* @public exported from `@promptbook/utils`
|
|
9960
|
+
*/
|
|
9961
|
+
const $isRunningInJest = new Function(`
|
|
9962
|
+
try {
|
|
9963
|
+
return process.env.JEST_WORKER_ID !== undefined;
|
|
9964
|
+
} catch (e) {
|
|
9965
|
+
return false;
|
|
9966
|
+
}
|
|
9967
|
+
`);
|
|
9968
|
+
/**
|
|
9969
|
+
* TODO: [šŗ]
|
|
9970
|
+
*/
|
|
9971
|
+
|
|
9972
|
+
/**
|
|
9973
|
+
* Detects if the code is running in a Node.js environment
|
|
9974
|
+
*
|
|
9975
|
+
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
9976
|
+
*
|
|
9977
|
+
* @public exported from `@promptbook/utils`
|
|
9978
|
+
*/
|
|
9979
|
+
const $isRunningInNode = new Function(`
|
|
9980
|
+
try {
|
|
9981
|
+
return this === global;
|
|
9982
|
+
} catch (e) {
|
|
9983
|
+
return false;
|
|
9984
|
+
}
|
|
9985
|
+
`);
|
|
9986
|
+
/**
|
|
9987
|
+
* TODO: [šŗ]
|
|
9988
|
+
*/
|
|
9989
|
+
|
|
9990
|
+
/**
|
|
9991
|
+
* Detects if the code is running in a web worker
|
|
9992
|
+
*
|
|
9993
|
+
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
9994
|
+
*
|
|
9995
|
+
* @public exported from `@promptbook/utils`
|
|
9996
|
+
*/
|
|
9997
|
+
const $isRunningInWebWorker = new Function(`
|
|
9998
|
+
try {
|
|
9999
|
+
if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
|
|
10000
|
+
return true;
|
|
10001
|
+
} else {
|
|
10002
|
+
return false;
|
|
10003
|
+
}
|
|
10004
|
+
} catch (e) {
|
|
10005
|
+
return false;
|
|
10006
|
+
}
|
|
10007
|
+
`);
|
|
10008
|
+
/**
|
|
10009
|
+
* TODO: [šŗ]
|
|
10010
|
+
*/
|
|
10011
|
+
|
|
10012
|
+
/**
|
|
10013
|
+
* Returns information about the current runtime environment
|
|
10014
|
+
*
|
|
10015
|
+
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environments
|
|
10016
|
+
*
|
|
10017
|
+
* @public exported from `@promptbook/utils`
|
|
10018
|
+
*/
|
|
10019
|
+
function $detectRuntimeEnvironment() {
|
|
10020
|
+
return {
|
|
10021
|
+
isRunningInBrowser: $isRunningInBrowser(),
|
|
10022
|
+
isRunningInJest: $isRunningInJest(),
|
|
10023
|
+
isRunningInNode: $isRunningInNode(),
|
|
10024
|
+
isRunningInWebWorker: $isRunningInWebWorker(),
|
|
10025
|
+
};
|
|
10026
|
+
}
|
|
10027
|
+
/**
|
|
10028
|
+
* TODO: [šŗ] Also detect and report node version here
|
|
10029
|
+
*/
|
|
10030
|
+
|
|
10031
|
+
/**
|
|
10032
|
+
* Simple wrapper `new Date().toISOString()`
|
|
10033
|
+
*
|
|
10034
|
+
* Note: `$` is used to indicate that this function is not a pure function - it is not deterministic because it depends on the current time
|
|
10035
|
+
*
|
|
10036
|
+
* @returns string_date branded type
|
|
10037
|
+
* @public exported from `@promptbook/utils`
|
|
10038
|
+
*/
|
|
10039
|
+
function $getCurrentDate() {
|
|
10040
|
+
return new Date().toISOString();
|
|
10041
|
+
}
|
|
10042
|
+
|
|
10043
|
+
/**
|
|
10044
|
+
* Function parseNumber will parse number from string
|
|
10045
|
+
*
|
|
10046
|
+
* Note: [š] This function is idempotent.
|
|
10047
|
+
* Unlike Number.parseInt, Number.parseFloat it will never ever result in NaN
|
|
10048
|
+
* Note: it also works only with decimal numbers
|
|
10049
|
+
*
|
|
10050
|
+
* @returns parsed number
|
|
10051
|
+
* @throws {ParseError} if the value is not a number
|
|
10052
|
+
*
|
|
10053
|
+
* @public exported from `@promptbook/utils`
|
|
10054
|
+
*/
|
|
10055
|
+
function parseNumber(value) {
|
|
10056
|
+
const originalValue = value;
|
|
10057
|
+
if (typeof value === 'number') {
|
|
10058
|
+
value = value.toString(); // <- TODO: Maybe more efficient way to do this
|
|
10059
|
+
}
|
|
10060
|
+
if (typeof value !== 'string') {
|
|
10061
|
+
return 0;
|
|
10062
|
+
}
|
|
10063
|
+
value = value.trim();
|
|
10064
|
+
if (value.startsWith('+')) {
|
|
10065
|
+
return parseNumber(value.substring(1));
|
|
10066
|
+
}
|
|
10067
|
+
if (value.startsWith('-')) {
|
|
10068
|
+
const number = parseNumber(value.substring(1));
|
|
10069
|
+
if (number === 0) {
|
|
10070
|
+
return 0; // <- Note: To prevent -0
|
|
10071
|
+
}
|
|
10072
|
+
return -number;
|
|
10073
|
+
}
|
|
10074
|
+
value = value.replace(/,/g, '.');
|
|
10075
|
+
value = value.toUpperCase();
|
|
10076
|
+
if (value === '') {
|
|
10077
|
+
return 0;
|
|
10078
|
+
}
|
|
10079
|
+
if (value === 'ā¾' || value.startsWith('INF')) {
|
|
10080
|
+
return Infinity;
|
|
10081
|
+
}
|
|
10082
|
+
if (value.includes('/')) {
|
|
10083
|
+
const [numerator_, denominator_] = value.split('/');
|
|
10084
|
+
const numerator = parseNumber(numerator_);
|
|
10085
|
+
const denominator = parseNumber(denominator_);
|
|
10086
|
+
if (denominator === 0) {
|
|
10087
|
+
throw new ParseError(`Unable to parse number from "${originalValue}" because denominator is zero`);
|
|
10088
|
+
}
|
|
10089
|
+
return numerator / denominator;
|
|
10090
|
+
}
|
|
10091
|
+
if (/^(NAN|NULL|NONE|UNDEFINED|ZERO|NO.*)$/.test(value)) {
|
|
10092
|
+
return 0;
|
|
10093
|
+
}
|
|
10094
|
+
if (value.includes('E')) {
|
|
10095
|
+
const [significand, exponent] = value.split('E');
|
|
10096
|
+
return parseNumber(significand) * 10 ** parseNumber(exponent);
|
|
10097
|
+
}
|
|
10098
|
+
if (!/^[0-9.]+$/.test(value) || value.split('.').length > 2) {
|
|
10099
|
+
throw new ParseError(`Unable to parse number from "${originalValue}"`);
|
|
10100
|
+
}
|
|
10101
|
+
const num = parseFloat(value);
|
|
10102
|
+
if (isNaN(num)) {
|
|
10103
|
+
throw new ParseError(`Unexpected NaN when parsing number from "${originalValue}"`);
|
|
10104
|
+
}
|
|
10105
|
+
return num;
|
|
10106
|
+
}
|
|
10107
|
+
/**
|
|
10108
|
+
* TODO: Maybe use sth. like safe-eval in fraction/calculation case @see https://www.npmjs.com/package/safe-eval
|
|
10109
|
+
* TODO: [š§ ][š»] Maybe export through `@promptbook/markdown-utils` not `@promptbook/utils`
|
|
10110
|
+
*/
|
|
10111
|
+
|
|
10112
|
+
/**
|
|
10113
|
+
* Removes quotes from a string
|
|
10114
|
+
*
|
|
10115
|
+
* Note: [š] This function is idempotent.
|
|
10116
|
+
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
10117
|
+
* Note: This function removes only the same quotes from the beginning and the end of the string
|
|
10118
|
+
* Note: There are two similar functions:
|
|
10119
|
+
* - `removeQuotes` which removes only bounding quotes
|
|
10120
|
+
* - `unwrapResult` which removes whole introduce sentence
|
|
10121
|
+
*
|
|
10122
|
+
* @param text optionally quoted text
|
|
10123
|
+
* @returns text without quotes
|
|
10124
|
+
* @public exported from `@promptbook/utils`
|
|
10125
|
+
*/
|
|
10126
|
+
function removeQuotes(text) {
|
|
10127
|
+
if (text.startsWith('"') && text.endsWith('"')) {
|
|
10128
|
+
return text.slice(1, -1);
|
|
10129
|
+
}
|
|
10130
|
+
if (text.startsWith("'") && text.endsWith("'")) {
|
|
10131
|
+
return text.slice(1, -1);
|
|
10132
|
+
}
|
|
10133
|
+
return text;
|
|
10134
|
+
}
|
|
10135
|
+
|
|
10136
|
+
/**
|
|
10137
|
+
* Trims string from all 4 sides
|
|
10138
|
+
*
|
|
10139
|
+
* Note: This is a re-exported function from the `spacetrim` package which is
|
|
10140
|
+
* Developed by same author @hejny as this package
|
|
10141
|
+
*
|
|
10142
|
+
* @public exported from `@promptbook/utils`
|
|
10143
|
+
* @see https://github.com/hejny/spacetrim#usage
|
|
10144
|
+
*/
|
|
10145
|
+
const spaceTrim = spaceTrim$1.spaceTrim;
|
|
10146
|
+
|
|
10147
|
+
/**
|
|
10148
|
+
* Checks if the given value is a valid JavaScript identifier name.
|
|
10149
|
+
*
|
|
10150
|
+
* @param javascriptName The value to check for JavaScript identifier validity.
|
|
10151
|
+
* @returns `true` if the value is a valid JavaScript name, false otherwise.
|
|
10152
|
+
* @public exported from `@promptbook/utils`
|
|
10153
|
+
*/
|
|
10154
|
+
function isValidJavascriptName(javascriptName) {
|
|
10155
|
+
if (typeof javascriptName !== 'string') {
|
|
10156
|
+
return false;
|
|
10157
|
+
}
|
|
10158
|
+
return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/i.test(javascriptName);
|
|
10159
|
+
}
|
|
10160
|
+
|
|
10161
|
+
/**
|
|
10162
|
+
* Normalizes agent name from arbitrary string to valid agent name
|
|
10163
|
+
*
|
|
10164
|
+
* Note: [š] This function is idempotent.
|
|
10165
|
+
*
|
|
10166
|
+
* @public exported from `@promptbook/core`
|
|
10167
|
+
*/
|
|
10168
|
+
function normalizeAgentName(rawAgentName) {
|
|
10169
|
+
return titleToName(spaceTrim__default["default"](rawAgentName));
|
|
10170
|
+
}
|
|
10171
|
+
|
|
10172
|
+
/**
|
|
10173
|
+
* Creates temporary default agent name based on agent source hash
|
|
10174
|
+
*
|
|
10175
|
+
* @public exported from `@promptbook/core`
|
|
10176
|
+
*/
|
|
10177
|
+
function createDefaultAgentName(agentSource) {
|
|
10178
|
+
const agentHash = computeAgentHash(agentSource);
|
|
10179
|
+
return normalizeAgentName(`Agent ${agentHash.substring(0, 6)}`);
|
|
10180
|
+
}
|
|
10181
|
+
|
|
10182
|
+
/**
|
|
10183
|
+
* Parses basic information from agent source
|
|
10184
|
+
*
|
|
10185
|
+
* There are 2 similar functions:
|
|
10186
|
+
* - `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.
|
|
10187
|
+
* - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronously.
|
|
10188
|
+
*
|
|
10189
|
+
* @public exported from `@promptbook/core`
|
|
10190
|
+
*/
|
|
10191
|
+
function parseAgentSource(agentSource) {
|
|
10192
|
+
const parseResult = parseAgentSourceWithCommitments(agentSource);
|
|
10193
|
+
// Find PERSONA and META commitments
|
|
10194
|
+
let personaDescription = null;
|
|
10195
|
+
for (const commitment of parseResult.commitments) {
|
|
10196
|
+
if (commitment.type !== 'PERSONA') {
|
|
10197
|
+
continue;
|
|
10198
|
+
}
|
|
10199
|
+
if (personaDescription === null) {
|
|
10200
|
+
personaDescription = '';
|
|
10201
|
+
}
|
|
10202
|
+
else {
|
|
10203
|
+
personaDescription += `\n\n${personaDescription}`;
|
|
10204
|
+
}
|
|
10205
|
+
personaDescription += commitment.content;
|
|
10206
|
+
}
|
|
10207
|
+
let initialMessage = null;
|
|
10208
|
+
for (const commitment of parseResult.commitments) {
|
|
10209
|
+
if (commitment.type !== 'INITIAL MESSAGE') {
|
|
10210
|
+
continue;
|
|
10211
|
+
}
|
|
10212
|
+
// Note: Initial message override logic - later overrides earlier
|
|
10213
|
+
// Or should it append? Usually initial message is just one block.
|
|
10214
|
+
// Let's stick to "later overrides earlier" for simplicity, or just take the last one.
|
|
10215
|
+
initialMessage = commitment.content;
|
|
10216
|
+
}
|
|
10217
|
+
const meta = {};
|
|
10218
|
+
const links = [];
|
|
10219
|
+
for (const commitment of parseResult.commitments) {
|
|
10220
|
+
if (commitment.type === 'META LINK') {
|
|
10221
|
+
links.push(spaceTrim__default["default"](commitment.content));
|
|
10222
|
+
continue;
|
|
10223
|
+
}
|
|
10224
|
+
if (commitment.type !== 'META') {
|
|
10225
|
+
continue;
|
|
10226
|
+
}
|
|
10227
|
+
// Parse META commitments - format is "META TYPE content"
|
|
10228
|
+
const metaTypeRaw = commitment.content.split(' ')[0] || 'NONE';
|
|
10229
|
+
if (metaTypeRaw === 'LINK') {
|
|
10230
|
+
links.push(spaceTrim__default["default"](commitment.content.substring(metaTypeRaw.length)));
|
|
10231
|
+
}
|
|
10232
|
+
const metaType = normalizeTo_camelCase(metaTypeRaw);
|
|
10233
|
+
meta[metaType] = spaceTrim__default["default"](commitment.content.substring(metaTypeRaw.length));
|
|
10234
|
+
}
|
|
10235
|
+
// Generate gravatar fallback if no meta image specified
|
|
10236
|
+
if (!meta.image) {
|
|
10237
|
+
meta.image = generatePlaceholderAgentProfileImageUrl(parseResult.agentName || '!!');
|
|
10238
|
+
}
|
|
10239
|
+
// Parse parameters using unified approach - both @Parameter and {parameter} notations
|
|
10240
|
+
// are treated as the same syntax feature with unified representation
|
|
10241
|
+
const parameters = parseParameters(agentSource);
|
|
10242
|
+
const agentHash = computeAgentHash(agentSource);
|
|
10243
|
+
return {
|
|
10244
|
+
agentName: normalizeAgentName(parseResult.agentName || createDefaultAgentName(agentSource)),
|
|
10245
|
+
agentHash,
|
|
10246
|
+
personaDescription,
|
|
10247
|
+
initialMessage,
|
|
10248
|
+
meta,
|
|
10249
|
+
links,
|
|
10250
|
+
parameters,
|
|
10251
|
+
};
|
|
10252
|
+
}
|
|
10253
|
+
/**
|
|
10254
|
+
* TODO: [š] Unite `AgentBasicInformation`, `ChatParticipant`, `LlmExecutionTools` + `LlmToolsMetadata`
|
|
10255
|
+
*/
|
|
10256
|
+
|
|
10257
|
+
/**
|
|
10258
|
+
* Creates model requirements for an agent based on its source
|
|
10259
|
+
*
|
|
10260
|
+
* There are 2 similar functions:
|
|
10261
|
+
* - `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.
|
|
10262
|
+
* - `createAgentModelRequirements` which is an asynchronous function that creates model requirements it applies each commitment one by one and works asynchronous.
|
|
10263
|
+
*
|
|
10264
|
+
* @public exported from `@promptbook/core`
|
|
10265
|
+
*/
|
|
10266
|
+
async function createAgentModelRequirements(agentSource, modelName, availableModels, llmTools) {
|
|
10267
|
+
// If availableModels are provided and no specific modelName is given,
|
|
10268
|
+
// use preparePersona to select the best model
|
|
10269
|
+
if (availableModels && !modelName && llmTools) {
|
|
10270
|
+
const selectedModelName = await selectBestModelUsingPersona(agentSource, llmTools);
|
|
10271
|
+
return createAgentModelRequirementsWithCommitments(agentSource, selectedModelName);
|
|
10272
|
+
}
|
|
10273
|
+
// Use the new commitment-based system with provided or default model
|
|
10274
|
+
return createAgentModelRequirementsWithCommitments(agentSource, modelName);
|
|
10275
|
+
}
|
|
10276
|
+
/**
|
|
10277
|
+
* Selects the best model using the preparePersona function
|
|
10278
|
+
* This directly uses preparePersona to ensure DRY principle
|
|
10279
|
+
*
|
|
10280
|
+
* @param agentSource The agent source to derive persona description from
|
|
10281
|
+
* @param llmTools LLM tools for preparing persona
|
|
10282
|
+
* @returns The name of the best selected model
|
|
10283
|
+
* @private function of `createAgentModelRequirements`
|
|
10284
|
+
*/
|
|
10285
|
+
async function selectBestModelUsingPersona(agentSource, llmTools) {
|
|
10286
|
+
var _a;
|
|
10287
|
+
// Parse agent source to get persona description
|
|
9755
10288
|
const { agentName, personaDescription } = parseAgentSource(agentSource);
|
|
9756
10289
|
// Use agent name as fallback if no persona description is available
|
|
9757
10290
|
const description = personaDescription || agentName || 'AI Agent';
|
|
@@ -9884,37 +10417,28 @@
|
|
|
9884
10417
|
PERSONA A friendly AI assistant that helps you with your tasks
|
|
9885
10418
|
`)));
|
|
9886
10419
|
// <- Note: Not using book`...` notation to avoid strange error in jest unit tests `TypeError: (0 , book_notation_1.book) is not a function`
|
|
9887
|
-
// <- TODO:
|
|
9888
|
-
// <-
|
|
9889
|
-
// <- TODO:
|
|
9890
|
-
|
|
9891
|
-
/**
|
|
9892
|
-
* Trims string from all 4 sides
|
|
9893
|
-
*
|
|
9894
|
-
* Note: This is a re-exported function from the `spacetrim` package which is
|
|
9895
|
-
* Developed by same author @hejny as this package
|
|
9896
|
-
*
|
|
9897
|
-
* @public exported from `@promptbook/utils`
|
|
9898
|
-
* @see https://github.com/hejny/spacetrim#usage
|
|
9899
|
-
*/
|
|
9900
|
-
const spaceTrim = spaceTrim$1.spaceTrim;
|
|
10420
|
+
// <- TODO: [š±āš] `GENESIS_BOOK` / `ADAM_BOOK` in `/agents/adam.book`
|
|
10421
|
+
// <- [š±āš] Buttons into genesis book
|
|
10422
|
+
// <- TODO: [š±āš] generateBookBoilerplate and deprecate `DEFAULT_BOOK`
|
|
9901
10423
|
|
|
10424
|
+
// import { getTableName } from '../../../../../apps/agents-server/src/database/getTableName';
|
|
10425
|
+
// <- TODO: [š±āš] Prevent imports from `/apps` -> `/src`
|
|
9902
10426
|
/**
|
|
9903
10427
|
* Agent collection stored in Supabase table
|
|
9904
10428
|
*
|
|
9905
10429
|
* Note: This object can work both from Node.js and browser environment depending on the Supabase client provided
|
|
9906
10430
|
*
|
|
9907
10431
|
* @public exported from `@promptbook/core`
|
|
9908
|
-
* <- TODO:
|
|
10432
|
+
* <- TODO: [š±āš] Move to `@promptbook/supabase` package
|
|
9909
10433
|
*/
|
|
9910
|
-
class AgentCollectionInSupabase /* TODO:
|
|
10434
|
+
class AgentCollectionInSupabase /* TODO: [š±āš] implements Agent */ {
|
|
9911
10435
|
/**
|
|
9912
10436
|
* @param rootPath - path to the directory with agents
|
|
9913
|
-
* @param tools - Execution tools to be used in
|
|
10437
|
+
* @param tools - Execution tools to be used in [š±āš] `Agent` itself and listing the agents
|
|
9914
10438
|
* @param options - Options for the collection creation
|
|
9915
10439
|
*/
|
|
9916
10440
|
constructor(supabaseClient,
|
|
9917
|
-
/// TODO:
|
|
10441
|
+
/// TODO: [š±āš] Remove> private readonly tools?: Pick<ExecutionTools, 'llm' | 'fs' | 'scrapers'>,
|
|
9918
10442
|
options) {
|
|
9919
10443
|
this.supabaseClient = supabaseClient;
|
|
9920
10444
|
this.options = options;
|
|
@@ -9929,8 +10453,8 @@
|
|
|
9929
10453
|
async listAgents( /* TODO: [š§ ] Allow to pass some condition here */) {
|
|
9930
10454
|
const { isVerbose = exports.DEFAULT_IS_VERBOSE } = this.options || {};
|
|
9931
10455
|
const selectResult = await this.supabaseClient
|
|
9932
|
-
.from('
|
|
9933
|
-
.select('agentProfile');
|
|
10456
|
+
.from(this.getTableName('Agent'))
|
|
10457
|
+
.select('agentName,agentProfile');
|
|
9934
10458
|
if (selectResult.error) {
|
|
9935
10459
|
throw new DatabaseError(spaceTrim((block) => `
|
|
9936
10460
|
|
|
@@ -9942,14 +10466,27 @@
|
|
|
9942
10466
|
if (isVerbose) {
|
|
9943
10467
|
console.info(`Found ${selectResult.data.length} agents in directory`);
|
|
9944
10468
|
}
|
|
9945
|
-
return selectResult.data.map((
|
|
10469
|
+
return selectResult.data.map(({ agentName, agentProfile }) => {
|
|
10470
|
+
if (isVerbose && agentProfile.agentName !== agentName) {
|
|
10471
|
+
console.warn(spaceTrim(`
|
|
10472
|
+
Agent name mismatch for agent "${agentName}". Using name from database.
|
|
10473
|
+
|
|
10474
|
+
agentName: "${agentName}"
|
|
10475
|
+
agentProfile.agentName: "${agentProfile.agentName}"
|
|
10476
|
+
`));
|
|
10477
|
+
}
|
|
10478
|
+
return {
|
|
10479
|
+
...agentProfile,
|
|
10480
|
+
agentName,
|
|
10481
|
+
};
|
|
10482
|
+
});
|
|
9946
10483
|
}
|
|
9947
10484
|
/**
|
|
9948
|
-
*
|
|
10485
|
+
* [š±āš]@@@
|
|
9949
10486
|
*/
|
|
9950
10487
|
async getAgentSource(agentName) {
|
|
9951
10488
|
const selectResult = await this.supabaseClient
|
|
9952
|
-
.from('
|
|
10489
|
+
.from(this.getTableName('Agent'))
|
|
9953
10490
|
.select('agentSource')
|
|
9954
10491
|
.eq('agentName', agentName)
|
|
9955
10492
|
.single();
|
|
@@ -9965,7 +10502,7 @@
|
|
|
9965
10502
|
|
|
9966
10503
|
${block(selectResult.error.message)}
|
|
9967
10504
|
`));
|
|
9968
|
-
// <- TODO:
|
|
10505
|
+
// <- TODO: [š±āš] First check if the error is "not found" and throw `NotFoundError` instead then throw `DatabaseError`
|
|
9969
10506
|
}
|
|
9970
10507
|
return selectResult.data.agentSource;
|
|
9971
10508
|
}
|
|
@@ -9977,67 +10514,90 @@
|
|
|
9977
10514
|
async createAgent(agentSource) {
|
|
9978
10515
|
const agentProfile = parseAgentSource(agentSource);
|
|
9979
10516
|
// <- TODO: [š]
|
|
9980
|
-
const
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
10517
|
+
const { agentName, agentHash } = agentProfile;
|
|
10518
|
+
const insertAgentResult = await this.supabaseClient.from(this.getTableName('Agent')).insert({
|
|
10519
|
+
agentName,
|
|
10520
|
+
agentHash,
|
|
9984
10521
|
agentProfile,
|
|
9985
10522
|
createdAt: new Date().toISOString(),
|
|
9986
10523
|
updatedAt: null,
|
|
9987
|
-
agentVersion: 0,
|
|
9988
10524
|
promptbookEngineVersion: PROMPTBOOK_ENGINE_VERSION,
|
|
9989
10525
|
usage: ZERO_USAGE,
|
|
9990
10526
|
agentSource: agentSource,
|
|
9991
10527
|
});
|
|
9992
|
-
if (
|
|
10528
|
+
if (insertAgentResult.error) {
|
|
9993
10529
|
throw new DatabaseError(spaceTrim((block) => `
|
|
9994
10530
|
Error creating agent "${agentProfile.agentName}" in Supabase:
|
|
9995
10531
|
|
|
9996
|
-
${block(
|
|
10532
|
+
${block(insertAgentResult.error.message)}
|
|
9997
10533
|
`));
|
|
9998
10534
|
}
|
|
10535
|
+
await this.supabaseClient.from(this.getTableName('AgentHistory')).insert({
|
|
10536
|
+
createdAt: new Date().toISOString(),
|
|
10537
|
+
agentName,
|
|
10538
|
+
agentHash,
|
|
10539
|
+
previousAgentHash: null,
|
|
10540
|
+
agentSource,
|
|
10541
|
+
promptbookEngineVersion: PROMPTBOOK_ENGINE_VERSION,
|
|
10542
|
+
});
|
|
10543
|
+
// <- TODO: [š§ ] What to do with `insertAgentHistoryResult.error`, ignore? wait?
|
|
9999
10544
|
return agentProfile;
|
|
10000
10545
|
}
|
|
10001
10546
|
/**
|
|
10002
10547
|
* Updates an existing agent in the collection
|
|
10003
10548
|
*/
|
|
10004
10549
|
async updateAgentSource(agentName, agentSource) {
|
|
10005
|
-
const
|
|
10006
|
-
.from('
|
|
10007
|
-
.select('
|
|
10550
|
+
const selectPreviousAgentResult = await this.supabaseClient
|
|
10551
|
+
.from(this.getTableName('Agent'))
|
|
10552
|
+
.select('agentHash,agentName')
|
|
10008
10553
|
.eq('agentName', agentName)
|
|
10009
10554
|
.single();
|
|
10010
|
-
if (
|
|
10011
|
-
throw new
|
|
10555
|
+
if (selectPreviousAgentResult.error) {
|
|
10556
|
+
throw new DatabaseError(spaceTrim((block) => `
|
|
10557
|
+
|
|
10558
|
+
Error fetching agent "${agentName}" from Supabase:
|
|
10559
|
+
|
|
10560
|
+
${block(selectPreviousAgentResult.error.message)}
|
|
10561
|
+
`));
|
|
10562
|
+
// <- TODO: [š±āš] First check if the error is "not found" and throw `NotFoundError` instead then throw `DatabaseError`
|
|
10012
10563
|
}
|
|
10564
|
+
selectPreviousAgentResult.data.agentName;
|
|
10565
|
+
const previousAgentHash = selectPreviousAgentResult.data.agentHash;
|
|
10013
10566
|
const agentProfile = parseAgentSource(agentSource);
|
|
10014
|
-
// TODO:
|
|
10015
|
-
|
|
10016
|
-
const
|
|
10017
|
-
|
|
10018
|
-
.from('AgentCollection' /* <- TODO: !!!! Change to `Agent` */)
|
|
10567
|
+
// <- TODO: [š]
|
|
10568
|
+
const { agentHash } = agentProfile;
|
|
10569
|
+
const updateAgentResult = await this.supabaseClient
|
|
10570
|
+
.from(this.getTableName('Agent'))
|
|
10019
10571
|
.update({
|
|
10020
|
-
// TODO:
|
|
10572
|
+
// TODO: [š±āš] Compare not update> agentName: agentProfile.agentName || '[š±āš]' /* <- TODO: [š±āš] Remove */,
|
|
10021
10573
|
agentProfile,
|
|
10022
10574
|
updatedAt: new Date().toISOString(),
|
|
10023
|
-
|
|
10575
|
+
agentHash: agentProfile.agentHash,
|
|
10024
10576
|
agentSource,
|
|
10025
10577
|
promptbookEngineVersion: PROMPTBOOK_ENGINE_VERSION,
|
|
10026
10578
|
})
|
|
10027
10579
|
.eq('agentName', agentName);
|
|
10028
|
-
|
|
10029
|
-
console.log('
|
|
10030
|
-
console.log('
|
|
10031
|
-
|
|
10032
|
-
if (updateResult.error) {
|
|
10580
|
+
// console.log('[š±āš] updateAgent', updateResult);
|
|
10581
|
+
// console.log('[š±āš] old', oldAgentSource);
|
|
10582
|
+
// console.log('[š±āš] new', newAgentSource);
|
|
10583
|
+
if (updateAgentResult.error) {
|
|
10033
10584
|
throw new DatabaseError(spaceTrim((block) => `
|
|
10034
10585
|
Error updating agent "${agentName}" in Supabase:
|
|
10035
10586
|
|
|
10036
|
-
${block(
|
|
10587
|
+
${block(updateAgentResult.error.message)}
|
|
10037
10588
|
`));
|
|
10038
10589
|
}
|
|
10590
|
+
await this.supabaseClient.from(this.getTableName('AgentHistory')).insert({
|
|
10591
|
+
createdAt: new Date().toISOString(),
|
|
10592
|
+
agentName,
|
|
10593
|
+
agentHash,
|
|
10594
|
+
previousAgentHash,
|
|
10595
|
+
agentSource,
|
|
10596
|
+
promptbookEngineVersion: PROMPTBOOK_ENGINE_VERSION,
|
|
10597
|
+
});
|
|
10598
|
+
// <- TODO: [š§ ] What to do with `insertAgentHistoryResult.error`, ignore? wait?
|
|
10039
10599
|
}
|
|
10040
|
-
// TODO:
|
|
10600
|
+
// TODO: [š±āš] public async getAgentSourceSubject(agentName: string_agent_name): Promise<BehaviorSubject<string_book>>
|
|
10041
10601
|
// Use Supabase realtime logic
|
|
10042
10602
|
/**
|
|
10043
10603
|
* Deletes an agent from the collection
|
|
@@ -10045,9 +10605,19 @@
|
|
|
10045
10605
|
async deleteAgent(agentName) {
|
|
10046
10606
|
throw new NotYetImplementedError('Method not implemented.');
|
|
10047
10607
|
}
|
|
10608
|
+
/**
|
|
10609
|
+
* Get the Supabase table name with prefix
|
|
10610
|
+
*
|
|
10611
|
+
* @param tableName - The original table name
|
|
10612
|
+
* @returns The prefixed table name
|
|
10613
|
+
*/
|
|
10614
|
+
getTableName(tableName) {
|
|
10615
|
+
const { tablePrefix = '' } = this.options || {};
|
|
10616
|
+
return `${tablePrefix}${tableName}`;
|
|
10617
|
+
}
|
|
10048
10618
|
}
|
|
10049
10619
|
/**
|
|
10050
|
-
* TODO:
|
|
10620
|
+
* TODO: [š±āš] Implement it here correctly and update JSDoc comments here, and on interface + other implementations
|
|
10051
10621
|
* TODO: Write unit test
|
|
10052
10622
|
* TODO: [š§ ][š] `AgentXxx` vs `AgentsXxx` naming convention
|
|
10053
10623
|
*/
|
|
@@ -10658,106 +11228,37 @@
|
|
|
10658
11228
|
*
|
|
10659
11229
|
* Note: `$` is used to indicate that this function mutates given `pipelineJson`
|
|
10660
11230
|
*/
|
|
10661
|
-
$applyToPipelineJson(command, $pipelineJson) {
|
|
10662
|
-
// TODO: Warn if the version is overridden
|
|
10663
|
-
$pipelineJson.bookVersion = command.bookVersion;
|
|
10664
|
-
},
|
|
10665
|
-
/**
|
|
10666
|
-
* Converts the BOOK_VERSION command back to string
|
|
10667
|
-
*
|
|
10668
|
-
* Note: This is used in `pipelineJsonToString` utility
|
|
10669
|
-
*/
|
|
10670
|
-
stringify(command) {
|
|
10671
|
-
return `---`; // <- TODO: [š] Implement
|
|
10672
|
-
},
|
|
10673
|
-
/**
|
|
10674
|
-
* Reads the BOOK_VERSION command from the `PipelineJson`
|
|
10675
|
-
*
|
|
10676
|
-
* Note: This is used in `pipelineJsonToString` utility
|
|
10677
|
-
*/
|
|
10678
|
-
takeFromPipelineJson(pipelineJson) {
|
|
10679
|
-
throw new NotYetImplementedError(`[š] Not implemented yet`); // <- TODO: [š] Implement
|
|
10680
|
-
},
|
|
10681
|
-
};
|
|
10682
|
-
|
|
10683
|
-
/**
|
|
10684
|
-
* Units of text measurement
|
|
10685
|
-
*
|
|
10686
|
-
* @see https://github.com/webgptorg/promptbook/discussions/30
|
|
10687
|
-
* @public exported from `@promptbook/core`
|
|
10688
|
-
*/
|
|
10689
|
-
const EXPECTATION_UNITS = ['CHARACTERS', 'WORDS', 'SENTENCES', 'LINES', 'PARAGRAPHS', 'PAGES'];
|
|
10690
|
-
/**
|
|
10691
|
-
* TODO: [š] Unite object for expecting amount and format - remove format
|
|
10692
|
-
*/
|
|
10693
|
-
|
|
10694
|
-
/**
|
|
10695
|
-
* Function parseNumber will parse number from string
|
|
10696
|
-
*
|
|
10697
|
-
* Note: [š] This function is idempotent.
|
|
10698
|
-
* Unlike Number.parseInt, Number.parseFloat it will never ever result in NaN
|
|
10699
|
-
* Note: it also works only with decimal numbers
|
|
10700
|
-
*
|
|
10701
|
-
* @returns parsed number
|
|
10702
|
-
* @throws {ParseError} if the value is not a number
|
|
10703
|
-
*
|
|
10704
|
-
* @public exported from `@promptbook/utils`
|
|
10705
|
-
*/
|
|
10706
|
-
function parseNumber(value) {
|
|
10707
|
-
const originalValue = value;
|
|
10708
|
-
if (typeof value === 'number') {
|
|
10709
|
-
value = value.toString(); // <- TODO: Maybe more efficient way to do this
|
|
10710
|
-
}
|
|
10711
|
-
if (typeof value !== 'string') {
|
|
10712
|
-
return 0;
|
|
10713
|
-
}
|
|
10714
|
-
value = value.trim();
|
|
10715
|
-
if (value.startsWith('+')) {
|
|
10716
|
-
return parseNumber(value.substring(1));
|
|
10717
|
-
}
|
|
10718
|
-
if (value.startsWith('-')) {
|
|
10719
|
-
const number = parseNumber(value.substring(1));
|
|
10720
|
-
if (number === 0) {
|
|
10721
|
-
return 0; // <- Note: To prevent -0
|
|
10722
|
-
}
|
|
10723
|
-
return -number;
|
|
10724
|
-
}
|
|
10725
|
-
value = value.replace(/,/g, '.');
|
|
10726
|
-
value = value.toUpperCase();
|
|
10727
|
-
if (value === '') {
|
|
10728
|
-
return 0;
|
|
10729
|
-
}
|
|
10730
|
-
if (value === 'ā¾' || value.startsWith('INF')) {
|
|
10731
|
-
return Infinity;
|
|
10732
|
-
}
|
|
10733
|
-
if (value.includes('/')) {
|
|
10734
|
-
const [numerator_, denominator_] = value.split('/');
|
|
10735
|
-
const numerator = parseNumber(numerator_);
|
|
10736
|
-
const denominator = parseNumber(denominator_);
|
|
10737
|
-
if (denominator === 0) {
|
|
10738
|
-
throw new ParseError(`Unable to parse number from "${originalValue}" because denominator is zero`);
|
|
10739
|
-
}
|
|
10740
|
-
return numerator / denominator;
|
|
10741
|
-
}
|
|
10742
|
-
if (/^(NAN|NULL|NONE|UNDEFINED|ZERO|NO.*)$/.test(value)) {
|
|
10743
|
-
return 0;
|
|
10744
|
-
}
|
|
10745
|
-
if (value.includes('E')) {
|
|
10746
|
-
const [significand, exponent] = value.split('E');
|
|
10747
|
-
return parseNumber(significand) * 10 ** parseNumber(exponent);
|
|
10748
|
-
}
|
|
10749
|
-
if (!/^[0-9.]+$/.test(value) || value.split('.').length > 2) {
|
|
10750
|
-
throw new ParseError(`Unable to parse number from "${originalValue}"`);
|
|
10751
|
-
}
|
|
10752
|
-
const num = parseFloat(value);
|
|
10753
|
-
if (isNaN(num)) {
|
|
10754
|
-
throw new ParseError(`Unexpected NaN when parsing number from "${originalValue}"`);
|
|
10755
|
-
}
|
|
10756
|
-
return num;
|
|
10757
|
-
}
|
|
11231
|
+
$applyToPipelineJson(command, $pipelineJson) {
|
|
11232
|
+
// TODO: Warn if the version is overridden
|
|
11233
|
+
$pipelineJson.bookVersion = command.bookVersion;
|
|
11234
|
+
},
|
|
11235
|
+
/**
|
|
11236
|
+
* Converts the BOOK_VERSION command back to string
|
|
11237
|
+
*
|
|
11238
|
+
* Note: This is used in `pipelineJsonToString` utility
|
|
11239
|
+
*/
|
|
11240
|
+
stringify(command) {
|
|
11241
|
+
return `---`; // <- TODO: [š] Implement
|
|
11242
|
+
},
|
|
11243
|
+
/**
|
|
11244
|
+
* Reads the BOOK_VERSION command from the `PipelineJson`
|
|
11245
|
+
*
|
|
11246
|
+
* Note: This is used in `pipelineJsonToString` utility
|
|
11247
|
+
*/
|
|
11248
|
+
takeFromPipelineJson(pipelineJson) {
|
|
11249
|
+
throw new NotYetImplementedError(`[š] Not implemented yet`); // <- TODO: [š] Implement
|
|
11250
|
+
},
|
|
11251
|
+
};
|
|
11252
|
+
|
|
10758
11253
|
/**
|
|
10759
|
-
*
|
|
10760
|
-
*
|
|
11254
|
+
* Units of text measurement
|
|
11255
|
+
*
|
|
11256
|
+
* @see https://github.com/webgptorg/promptbook/discussions/30
|
|
11257
|
+
* @public exported from `@promptbook/core`
|
|
11258
|
+
*/
|
|
11259
|
+
const EXPECTATION_UNITS = ['CHARACTERS', 'WORDS', 'SENTENCES', 'LINES', 'PARAGRAPHS', 'PAGES'];
|
|
11260
|
+
/**
|
|
11261
|
+
* TODO: [š] Unite object for expecting amount and format - remove format
|
|
10761
11262
|
*/
|
|
10762
11263
|
|
|
10763
11264
|
/**
|
|
@@ -10902,30 +11403,6 @@
|
|
|
10902
11403
|
},
|
|
10903
11404
|
};
|
|
10904
11405
|
|
|
10905
|
-
/**
|
|
10906
|
-
* Removes quotes from a string
|
|
10907
|
-
*
|
|
10908
|
-
* Note: [š] This function is idempotent.
|
|
10909
|
-
* Tip: This is very useful for post-processing of the result of the LLM model
|
|
10910
|
-
* Note: This function removes only the same quotes from the beginning and the end of the string
|
|
10911
|
-
* Note: There are two similar functions:
|
|
10912
|
-
* - `removeQuotes` which removes only bounding quotes
|
|
10913
|
-
* - `unwrapResult` which removes whole introduce sentence
|
|
10914
|
-
*
|
|
10915
|
-
* @param text optionally quoted text
|
|
10916
|
-
* @returns text without quotes
|
|
10917
|
-
* @public exported from `@promptbook/utils`
|
|
10918
|
-
*/
|
|
10919
|
-
function removeQuotes(text) {
|
|
10920
|
-
if (text.startsWith('"') && text.endsWith('"')) {
|
|
10921
|
-
return text.slice(1, -1);
|
|
10922
|
-
}
|
|
10923
|
-
if (text.startsWith("'") && text.endsWith("'")) {
|
|
10924
|
-
return text.slice(1, -1);
|
|
10925
|
-
}
|
|
10926
|
-
return text;
|
|
10927
|
-
}
|
|
10928
|
-
|
|
10929
11406
|
/**
|
|
10930
11407
|
* Function `validateParameterName` will normalize and validate a parameter name for use in pipelines.
|
|
10931
11408
|
* It removes diacritics, emojis, and quotes, normalizes to camelCase, and checks for reserved names and invalid characters.
|
|
@@ -12112,20 +12589,6 @@
|
|
|
12112
12589
|
persona.description += spaceTrim__default["default"]('\n\n' + personaDescription);
|
|
12113
12590
|
}
|
|
12114
12591
|
|
|
12115
|
-
/**
|
|
12116
|
-
* Checks if the given value is a valid JavaScript identifier name.
|
|
12117
|
-
*
|
|
12118
|
-
* @param javascriptName The value to check for JavaScript identifier validity.
|
|
12119
|
-
* @returns `true` if the value is a valid JavaScript name, false otherwise.
|
|
12120
|
-
* @public exported from `@promptbook/utils`
|
|
12121
|
-
*/
|
|
12122
|
-
function isValidJavascriptName(javascriptName) {
|
|
12123
|
-
if (typeof javascriptName !== 'string') {
|
|
12124
|
-
return false;
|
|
12125
|
-
}
|
|
12126
|
-
return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/i.test(javascriptName);
|
|
12127
|
-
}
|
|
12128
|
-
|
|
12129
12592
|
/**
|
|
12130
12593
|
* Parses the postprocess command
|
|
12131
12594
|
*
|
|
@@ -13694,114 +14157,6 @@
|
|
|
13694
14157
|
* TODO: [š] This can be part of markdown builder
|
|
13695
14158
|
*/
|
|
13696
14159
|
|
|
13697
|
-
/**
|
|
13698
|
-
* Creates a Mermaid graph based on the promptbook
|
|
13699
|
-
*
|
|
13700
|
-
* Note: The result is not wrapped in a Markdown code block
|
|
13701
|
-
*
|
|
13702
|
-
* @public exported from `@promptbook/utils`
|
|
13703
|
-
*/
|
|
13704
|
-
function renderPromptbookMermaid(pipelineJson, options) {
|
|
13705
|
-
const { linkTask = () => null } = options || {};
|
|
13706
|
-
const MERMAID_PREFIX = 'pipeline_';
|
|
13707
|
-
const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
|
|
13708
|
-
const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
|
|
13709
|
-
const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
|
|
13710
|
-
const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
|
|
13711
|
-
const parameterNameToTaskName = (parameterName) => {
|
|
13712
|
-
if (parameterName === 'knowledge') {
|
|
13713
|
-
return MERMAID_KNOWLEDGE_NAME;
|
|
13714
|
-
}
|
|
13715
|
-
else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
13716
|
-
return MERMAID_RESERVED_NAME;
|
|
13717
|
-
}
|
|
13718
|
-
const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
|
|
13719
|
-
if (!parameter) {
|
|
13720
|
-
throw new UnexpectedError(`Could not find {${parameterName}}`);
|
|
13721
|
-
// <- TODO: This causes problems when {knowledge} and other reserved parameters are used
|
|
13722
|
-
}
|
|
13723
|
-
if (parameter.isInput) {
|
|
13724
|
-
return MERMAID_INPUT_NAME;
|
|
13725
|
-
}
|
|
13726
|
-
const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
|
|
13727
|
-
if (!task) {
|
|
13728
|
-
throw new Error(`Could not find task for {${parameterName}}`);
|
|
13729
|
-
}
|
|
13730
|
-
return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
|
|
13731
|
-
};
|
|
13732
|
-
const inputAndIntermediateParametersMermaid = pipelineJson.tasks
|
|
13733
|
-
.flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
|
|
13734
|
-
`${parameterNameToTaskName(resultingParameterName)}("${title}")`,
|
|
13735
|
-
...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
|
|
13736
|
-
])
|
|
13737
|
-
.join('\n');
|
|
13738
|
-
const outputParametersMermaid = pipelineJson.parameters
|
|
13739
|
-
.filter(({ isOutput }) => isOutput)
|
|
13740
|
-
.map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
|
|
13741
|
-
.join('\n');
|
|
13742
|
-
const linksMermaid = pipelineJson.tasks
|
|
13743
|
-
.map((task) => {
|
|
13744
|
-
const link = linkTask(task);
|
|
13745
|
-
if (link === null) {
|
|
13746
|
-
return '';
|
|
13747
|
-
}
|
|
13748
|
-
const { href, title } = link;
|
|
13749
|
-
const taskName = parameterNameToTaskName(task.resultingParameterName);
|
|
13750
|
-
return `click ${taskName} href "${href}" "${title}";`;
|
|
13751
|
-
})
|
|
13752
|
-
.filter((line) => line !== '')
|
|
13753
|
-
.join('\n');
|
|
13754
|
-
const interactionPointsMermaid = Object.entries({
|
|
13755
|
-
[MERMAID_INPUT_NAME]: 'Input',
|
|
13756
|
-
[MERMAID_OUTPUT_NAME]: 'Output',
|
|
13757
|
-
[MERMAID_RESERVED_NAME]: 'Other',
|
|
13758
|
-
[MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
|
|
13759
|
-
})
|
|
13760
|
-
.filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
|
|
13761
|
-
.map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
|
|
13762
|
-
.join('\n');
|
|
13763
|
-
const promptbookMermaid = spaceTrim$1.spaceTrim((block) => `
|
|
13764
|
-
|
|
13765
|
-
%% š® Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
|
|
13766
|
-
|
|
13767
|
-
flowchart LR
|
|
13768
|
-
subgraph "${pipelineJson.title}"
|
|
13769
|
-
|
|
13770
|
-
%% Basic configuration
|
|
13771
|
-
direction TB
|
|
13772
|
-
|
|
13773
|
-
%% Interaction points from pipeline to outside
|
|
13774
|
-
${block(interactionPointsMermaid)}
|
|
13775
|
-
|
|
13776
|
-
%% Input and intermediate parameters
|
|
13777
|
-
${block(inputAndIntermediateParametersMermaid)}
|
|
13778
|
-
|
|
13779
|
-
|
|
13780
|
-
%% Output parameters
|
|
13781
|
-
${block(outputParametersMermaid)}
|
|
13782
|
-
|
|
13783
|
-
%% Links
|
|
13784
|
-
${block(linksMermaid)}
|
|
13785
|
-
|
|
13786
|
-
%% Styles
|
|
13787
|
-
classDef ${MERMAID_INPUT_NAME} color: grey;
|
|
13788
|
-
classDef ${MERMAID_OUTPUT_NAME} color: grey;
|
|
13789
|
-
classDef ${MERMAID_RESERVED_NAME} color: grey;
|
|
13790
|
-
classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
|
|
13791
|
-
|
|
13792
|
-
end;
|
|
13793
|
-
|
|
13794
|
-
`);
|
|
13795
|
-
return promptbookMermaid;
|
|
13796
|
-
}
|
|
13797
|
-
/**
|
|
13798
|
-
* TODO: [š§ ] FOREACH in mermaid graph
|
|
13799
|
-
* TODO: [š§ ] Knowledge in mermaid graph
|
|
13800
|
-
* TODO: [š§ ] Personas in mermaid graph
|
|
13801
|
-
* TODO: Maybe use some Mermaid package instead of string templating
|
|
13802
|
-
* TODO: [š] When more than 2 functionalities, split into separate functions
|
|
13803
|
-
*/
|
|
13804
|
-
|
|
13805
14160
|
/**
|
|
13806
14161
|
* Prettyfies Promptbook string and adds Mermaid graph
|
|
13807
14162
|
*
|
|
@@ -14362,64 +14717,6 @@
|
|
|
14362
14717
|
* TODO: [Ā®] DRY Register logic
|
|
14363
14718
|
*/
|
|
14364
14719
|
|
|
14365
|
-
/**
|
|
14366
|
-
* Detects if the code is running in a browser environment in main thread (Not in a web worker)
|
|
14367
|
-
*
|
|
14368
|
-
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
14369
|
-
*
|
|
14370
|
-
* @public exported from `@promptbook/utils`
|
|
14371
|
-
*/
|
|
14372
|
-
const $isRunningInBrowser = new Function(`
|
|
14373
|
-
try {
|
|
14374
|
-
return this === window;
|
|
14375
|
-
} catch (e) {
|
|
14376
|
-
return false;
|
|
14377
|
-
}
|
|
14378
|
-
`);
|
|
14379
|
-
/**
|
|
14380
|
-
* TODO: [šŗ]
|
|
14381
|
-
*/
|
|
14382
|
-
|
|
14383
|
-
/**
|
|
14384
|
-
* Detects if the code is running in a Node.js environment
|
|
14385
|
-
*
|
|
14386
|
-
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
14387
|
-
*
|
|
14388
|
-
* @public exported from `@promptbook/utils`
|
|
14389
|
-
*/
|
|
14390
|
-
const $isRunningInNode = new Function(`
|
|
14391
|
-
try {
|
|
14392
|
-
return this === global;
|
|
14393
|
-
} catch (e) {
|
|
14394
|
-
return false;
|
|
14395
|
-
}
|
|
14396
|
-
`);
|
|
14397
|
-
/**
|
|
14398
|
-
* TODO: [šŗ]
|
|
14399
|
-
*/
|
|
14400
|
-
|
|
14401
|
-
/**
|
|
14402
|
-
* Detects if the code is running in a web worker
|
|
14403
|
-
*
|
|
14404
|
-
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
14405
|
-
*
|
|
14406
|
-
* @public exported from `@promptbook/utils`
|
|
14407
|
-
*/
|
|
14408
|
-
const $isRunningInWebWorker = new Function(`
|
|
14409
|
-
try {
|
|
14410
|
-
if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) {
|
|
14411
|
-
return true;
|
|
14412
|
-
} else {
|
|
14413
|
-
return false;
|
|
14414
|
-
}
|
|
14415
|
-
} catch (e) {
|
|
14416
|
-
return false;
|
|
14417
|
-
}
|
|
14418
|
-
`);
|
|
14419
|
-
/**
|
|
14420
|
-
* TODO: [šŗ]
|
|
14421
|
-
*/
|
|
14422
|
-
|
|
14423
14720
|
/**
|
|
14424
14721
|
* Creates a message with all registered LLM tools
|
|
14425
14722
|
*
|
|
@@ -14653,18 +14950,6 @@
|
|
|
14653
14950
|
}
|
|
14654
14951
|
}
|
|
14655
14952
|
|
|
14656
|
-
/**
|
|
14657
|
-
* Simple wrapper `new Date().toISOString()`
|
|
14658
|
-
*
|
|
14659
|
-
* Note: `$` is used to indicate that this function is not a pure function - it is not deterministic because it depends on the current time
|
|
14660
|
-
*
|
|
14661
|
-
* @returns string_date branded type
|
|
14662
|
-
* @public exported from `@promptbook/utils`
|
|
14663
|
-
*/
|
|
14664
|
-
function $getCurrentDate() {
|
|
14665
|
-
return new Date().toISOString();
|
|
14666
|
-
}
|
|
14667
|
-
|
|
14668
14953
|
/**
|
|
14669
14954
|
* Intercepts LLM tools and counts total usage of the tools
|
|
14670
14955
|
*
|
|
@@ -15290,18 +15575,18 @@
|
|
|
15290
15575
|
},
|
|
15291
15576
|
},
|
|
15292
15577
|
/**/
|
|
15293
|
-
/*/
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
|
|
15297
|
-
|
|
15298
|
-
|
|
15299
|
-
/*/
|
|
15300
|
-
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
|
|
15304
|
-
|
|
15578
|
+
/*/
|
|
15579
|
+
{
|
|
15580
|
+
modelTitle: 'tts-1-hd-1106',
|
|
15581
|
+
modelName: 'tts-1-hd-1106',
|
|
15582
|
+
},
|
|
15583
|
+
/**/
|
|
15584
|
+
/*/
|
|
15585
|
+
{
|
|
15586
|
+
modelTitle: 'tts-1-hd',
|
|
15587
|
+
modelName: 'tts-1-hd',
|
|
15588
|
+
},
|
|
15589
|
+
/**/
|
|
15305
15590
|
/**/
|
|
15306
15591
|
{
|
|
15307
15592
|
modelVariant: 'CHAT',
|
|
@@ -16487,11 +16772,12 @@
|
|
|
16487
16772
|
*
|
|
16488
16773
|
* This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
|
|
16489
16774
|
*
|
|
16490
|
-
*
|
|
16775
|
+
* Note: [š¦] There are several different things in Promptbook:
|
|
16491
16776
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
16492
16777
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
16493
16778
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
16494
16779
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
16780
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
16495
16781
|
*
|
|
16496
16782
|
* @public exported from `@promptbook/openai`
|
|
16497
16783
|
*/
|
|
@@ -16526,6 +16812,12 @@
|
|
|
16526
16812
|
* Calls OpenAI API to use a chat model.
|
|
16527
16813
|
*/
|
|
16528
16814
|
async callChatModel(prompt) {
|
|
16815
|
+
return this.callChatModelStream(prompt, () => { });
|
|
16816
|
+
}
|
|
16817
|
+
/**
|
|
16818
|
+
* Calls OpenAI API to use a chat model with streaming.
|
|
16819
|
+
*/
|
|
16820
|
+
async callChatModelStream(prompt, onProgress) {
|
|
16529
16821
|
var _a, _b, _c;
|
|
16530
16822
|
if (this.options.isVerbose) {
|
|
16531
16823
|
console.info('š¬ OpenAI callChatModel call', { prompt });
|
|
@@ -16593,21 +16885,24 @@
|
|
|
16593
16885
|
console.info('connect', stream.currentEvent);
|
|
16594
16886
|
}
|
|
16595
16887
|
});
|
|
16596
|
-
|
|
16597
|
-
|
|
16598
|
-
|
|
16599
|
-
this.options.isVerbose &&
|
|
16600
|
-
messageDelta &&
|
|
16601
|
-
messageDelta.content &&
|
|
16602
|
-
messageDelta.content[0] &&
|
|
16603
|
-
messageDelta.content[0].type === 'text'
|
|
16604
|
-
) {
|
|
16605
|
-
console.info('messageDelta', messageDelta.content[0].text?.value);
|
|
16888
|
+
stream.on('textDelta', (textDelta, snapshot) => {
|
|
16889
|
+
if (this.options.isVerbose && textDelta.value) {
|
|
16890
|
+
console.info('textDelta', textDelta.value);
|
|
16606
16891
|
}
|
|
16607
|
-
|
|
16608
|
-
|
|
16892
|
+
const chunk = {
|
|
16893
|
+
content: textDelta.value || '',
|
|
16894
|
+
modelName: 'assistant',
|
|
16895
|
+
timing: {
|
|
16896
|
+
start,
|
|
16897
|
+
complete: $getCurrentDate(),
|
|
16898
|
+
},
|
|
16899
|
+
usage: UNCERTAIN_USAGE,
|
|
16900
|
+
rawPromptContent,
|
|
16901
|
+
rawRequest,
|
|
16902
|
+
rawResponse: snapshot,
|
|
16903
|
+
};
|
|
16904
|
+
onProgress(chunk);
|
|
16609
16905
|
});
|
|
16610
|
-
*/
|
|
16611
16906
|
stream.on('messageCreated', (message) => {
|
|
16612
16907
|
if (this.options.isVerbose) {
|
|
16613
16908
|
console.info('messageCreated', message);
|
|
@@ -16643,7 +16938,7 @@
|
|
|
16643
16938
|
}
|
|
16644
16939
|
return exportJson({
|
|
16645
16940
|
name: 'promptResult',
|
|
16646
|
-
message: `Result of \`OpenAiAssistantExecutionTools.
|
|
16941
|
+
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
|
|
16647
16942
|
order: [],
|
|
16648
16943
|
value: {
|
|
16649
16944
|
content: resultContent,
|
|
@@ -16662,15 +16957,19 @@
|
|
|
16662
16957
|
},
|
|
16663
16958
|
});
|
|
16664
16959
|
}
|
|
16665
|
-
|
|
16960
|
+
/*
|
|
16961
|
+
public async playground() {
|
|
16666
16962
|
const client = await this.getClient();
|
|
16963
|
+
|
|
16667
16964
|
// List all assistants
|
|
16668
16965
|
const assistants = await client.beta.assistants.list();
|
|
16669
16966
|
console.log('!!! Assistants:', assistants);
|
|
16967
|
+
|
|
16670
16968
|
// Get details of a specific assistant
|
|
16671
16969
|
const assistantId = 'asst_MO8fhZf4dGloCfXSHeLcIik0';
|
|
16672
16970
|
const assistant = await client.beta.assistants.retrieve(assistantId);
|
|
16673
16971
|
console.log('!!! Assistant Details:', assistant);
|
|
16972
|
+
|
|
16674
16973
|
// Update an assistant
|
|
16675
16974
|
const updatedAssistant = await client.beta.assistants.update(assistantId, {
|
|
16676
16975
|
name: assistant.name + '(M)',
|
|
@@ -16680,7 +16979,18 @@
|
|
|
16680
16979
|
},
|
|
16681
16980
|
});
|
|
16682
16981
|
console.log('!!! Updated Assistant:', updatedAssistant);
|
|
16683
|
-
|
|
16982
|
+
|
|
16983
|
+
await forEver();
|
|
16984
|
+
}
|
|
16985
|
+
*/
|
|
16986
|
+
/**
|
|
16987
|
+
* Get an existing assistant tool wrapper
|
|
16988
|
+
*/
|
|
16989
|
+
getAssistant(assistantId) {
|
|
16990
|
+
return new OpenAiAssistantExecutionTools({
|
|
16991
|
+
...this.options,
|
|
16992
|
+
assistantId,
|
|
16993
|
+
});
|
|
16684
16994
|
}
|
|
16685
16995
|
async createNewAssistant(options) {
|
|
16686
16996
|
if (!this.isCreatingNewAssistantsAllowed) {
|
|
@@ -16767,9 +17077,98 @@
|
|
|
16767
17077
|
}
|
|
16768
17078
|
const assistant = await client.beta.assistants.create(assistantConfig);
|
|
16769
17079
|
console.log(`ā
Assistant created: ${assistant.id}`);
|
|
16770
|
-
// TODO:
|
|
16771
|
-
// TODO:
|
|
16772
|
-
// TODO:
|
|
17080
|
+
// TODO: [š±āš] Try listing existing assistants
|
|
17081
|
+
// TODO: [š±āš] Try marking existing assistants by DISCRIMINANT
|
|
17082
|
+
// TODO: [š±āš] Allow to update and reconnect to existing assistants
|
|
17083
|
+
return new OpenAiAssistantExecutionTools({
|
|
17084
|
+
...this.options,
|
|
17085
|
+
isCreatingNewAssistantsAllowed: false,
|
|
17086
|
+
assistantId: assistant.id,
|
|
17087
|
+
});
|
|
17088
|
+
}
|
|
17089
|
+
async updateAssistant(options) {
|
|
17090
|
+
if (!this.isCreatingNewAssistantsAllowed) {
|
|
17091
|
+
throw new NotAllowed(`Updating assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
|
|
17092
|
+
}
|
|
17093
|
+
const { assistantId, name, instructions, knowledgeSources } = options;
|
|
17094
|
+
const client = await this.getClient();
|
|
17095
|
+
let vectorStoreId;
|
|
17096
|
+
// If knowledge sources are provided, create a vector store with them
|
|
17097
|
+
// TODO: [š§ ] Reuse vector store creation logic from createNewAssistant
|
|
17098
|
+
if (knowledgeSources && knowledgeSources.length > 0) {
|
|
17099
|
+
if (this.options.isVerbose) {
|
|
17100
|
+
console.info(`š Creating vector store for update with ${knowledgeSources.length} knowledge sources...`);
|
|
17101
|
+
}
|
|
17102
|
+
// Create a vector store
|
|
17103
|
+
const vectorStore = await client.beta.vectorStores.create({
|
|
17104
|
+
name: `${name} Knowledge Base`,
|
|
17105
|
+
});
|
|
17106
|
+
vectorStoreId = vectorStore.id;
|
|
17107
|
+
if (this.options.isVerbose) {
|
|
17108
|
+
console.info(`ā
Vector store created: ${vectorStoreId}`);
|
|
17109
|
+
}
|
|
17110
|
+
// Upload files from knowledge sources to the vector store
|
|
17111
|
+
const fileStreams = [];
|
|
17112
|
+
for (const source of knowledgeSources) {
|
|
17113
|
+
try {
|
|
17114
|
+
// Check if it's a URL
|
|
17115
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
17116
|
+
// Download the file
|
|
17117
|
+
const response = await fetch(source);
|
|
17118
|
+
if (!response.ok) {
|
|
17119
|
+
console.error(`Failed to download ${source}: ${response.statusText}`);
|
|
17120
|
+
continue;
|
|
17121
|
+
}
|
|
17122
|
+
const buffer = await response.arrayBuffer();
|
|
17123
|
+
const filename = source.split('/').pop() || 'downloaded-file';
|
|
17124
|
+
const blob = new Blob([buffer]);
|
|
17125
|
+
const file = new File([blob], filename);
|
|
17126
|
+
fileStreams.push(file);
|
|
17127
|
+
}
|
|
17128
|
+
else {
|
|
17129
|
+
// Assume it's a local file path
|
|
17130
|
+
// Note: This will work in Node.js environment
|
|
17131
|
+
// For browser environments, this would need different handling
|
|
17132
|
+
const fs = await import('fs');
|
|
17133
|
+
const fileStream = fs.createReadStream(source);
|
|
17134
|
+
fileStreams.push(fileStream);
|
|
17135
|
+
}
|
|
17136
|
+
}
|
|
17137
|
+
catch (error) {
|
|
17138
|
+
console.error(`Error processing knowledge source ${source}:`, error);
|
|
17139
|
+
}
|
|
17140
|
+
}
|
|
17141
|
+
// Batch upload files to the vector store
|
|
17142
|
+
if (fileStreams.length > 0) {
|
|
17143
|
+
try {
|
|
17144
|
+
await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
|
|
17145
|
+
files: fileStreams,
|
|
17146
|
+
});
|
|
17147
|
+
if (this.options.isVerbose) {
|
|
17148
|
+
console.info(`ā
Uploaded ${fileStreams.length} files to vector store`);
|
|
17149
|
+
}
|
|
17150
|
+
}
|
|
17151
|
+
catch (error) {
|
|
17152
|
+
console.error('Error uploading files to vector store:', error);
|
|
17153
|
+
}
|
|
17154
|
+
}
|
|
17155
|
+
}
|
|
17156
|
+
const assistantUpdate = {
|
|
17157
|
+
name,
|
|
17158
|
+
instructions,
|
|
17159
|
+
tools: [/* TODO: [š§ ] Maybe add { type: 'code_interpreter' }, */ { type: 'file_search' }],
|
|
17160
|
+
};
|
|
17161
|
+
if (vectorStoreId) {
|
|
17162
|
+
assistantUpdate.tool_resources = {
|
|
17163
|
+
file_search: {
|
|
17164
|
+
vector_store_ids: [vectorStoreId],
|
|
17165
|
+
},
|
|
17166
|
+
};
|
|
17167
|
+
}
|
|
17168
|
+
const assistant = await client.beta.assistants.update(assistantId, assistantUpdate);
|
|
17169
|
+
if (this.options.isVerbose) {
|
|
17170
|
+
console.log(`ā
Assistant updated: ${assistant.id}`);
|
|
17171
|
+
}
|
|
16773
17172
|
return new OpenAiAssistantExecutionTools({
|
|
16774
17173
|
...this.options,
|
|
16775
17174
|
isCreatingNewAssistantsAllowed: false,
|
|
@@ -16808,11 +17207,12 @@
|
|
|
16808
17207
|
* Execution Tools for calling LLM models with a predefined agent "soul"
|
|
16809
17208
|
* This wraps underlying LLM execution tools and applies agent-specific system prompts and requirements
|
|
16810
17209
|
*
|
|
16811
|
-
*
|
|
17210
|
+
* Note: [š¦] There are several different things in Promptbook:
|
|
16812
17211
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
16813
17212
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
16814
17213
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
16815
17214
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
17215
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
16816
17216
|
*
|
|
16817
17217
|
* @public exported from `@promptbook/core`
|
|
16818
17218
|
*/
|
|
@@ -16906,9 +17306,12 @@
|
|
|
16906
17306
|
* Calls the chat model with agent-specific system prompt and requirements
|
|
16907
17307
|
*/
|
|
16908
17308
|
async callChatModel(prompt) {
|
|
16909
|
-
|
|
16910
|
-
|
|
16911
|
-
|
|
17309
|
+
return this.callChatModelStream(prompt, () => { });
|
|
17310
|
+
}
|
|
17311
|
+
/**
|
|
17312
|
+
* Calls the chat model with agent-specific system prompt and requirements with streaming
|
|
17313
|
+
*/
|
|
17314
|
+
async callChatModelStream(prompt, onProgress) {
|
|
16912
17315
|
// Ensure we're working with a chat prompt
|
|
16913
17316
|
if (prompt.modelRequirements.modelVariant !== 'CHAT') {
|
|
16914
17317
|
throw new Error('AgentLlmExecutionTools only supports chat prompts');
|
|
@@ -16917,27 +17320,58 @@
|
|
|
16917
17320
|
const chatPrompt = prompt;
|
|
16918
17321
|
let underlyingLlmResult;
|
|
16919
17322
|
if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
|
|
16920
|
-
|
|
16921
|
-
|
|
17323
|
+
const requirementsHash = cryptoJs.SHA256(JSON.stringify(modelRequirements)).toString();
|
|
17324
|
+
const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
|
|
17325
|
+
let assistant;
|
|
17326
|
+
if (cached) {
|
|
17327
|
+
if (cached.requirementsHash === requirementsHash) {
|
|
17328
|
+
if (this.options.isVerbose) {
|
|
17329
|
+
console.log(`1ļøā£ Using cached OpenAI Assistant for agent ${this.title}...`);
|
|
17330
|
+
}
|
|
17331
|
+
assistant = this.options.llmTools.getAssistant(cached.assistantId);
|
|
17332
|
+
}
|
|
17333
|
+
else {
|
|
17334
|
+
if (this.options.isVerbose) {
|
|
17335
|
+
console.log(`1ļøā£ Updating OpenAI Assistant for agent ${this.title}...`);
|
|
17336
|
+
}
|
|
17337
|
+
assistant = await this.options.llmTools.updateAssistant({
|
|
17338
|
+
assistantId: cached.assistantId,
|
|
17339
|
+
name: this.title,
|
|
17340
|
+
instructions: modelRequirements.systemMessage,
|
|
17341
|
+
knowledgeSources: modelRequirements.knowledgeSources,
|
|
17342
|
+
});
|
|
17343
|
+
AgentLlmExecutionTools.assistantCache.set(this.title, {
|
|
17344
|
+
assistantId: assistant.assistantId,
|
|
17345
|
+
requirementsHash,
|
|
17346
|
+
});
|
|
17347
|
+
}
|
|
16922
17348
|
}
|
|
16923
|
-
|
|
16924
|
-
|
|
16925
|
-
|
|
16926
|
-
instructions: modelRequirements.systemMessage,
|
|
16927
|
-
knowledgeSources: modelRequirements.knowledgeSources,
|
|
16928
|
-
/*
|
|
16929
|
-
!!!
|
|
16930
|
-
metadata: {
|
|
16931
|
-
agentModelName: this.modelName,
|
|
17349
|
+
else {
|
|
17350
|
+
if (this.options.isVerbose) {
|
|
17351
|
+
console.log(`1ļøā£ Creating new OpenAI Assistant for agent ${this.title}...`);
|
|
16932
17352
|
}
|
|
16933
|
-
|
|
16934
|
-
|
|
16935
|
-
|
|
16936
|
-
|
|
17353
|
+
// <- TODO: [š±āš] Check also `isCreatingNewAssistantsAllowed` and warn about it
|
|
17354
|
+
assistant = await this.options.llmTools.createNewAssistant({
|
|
17355
|
+
name: this.title,
|
|
17356
|
+
instructions: modelRequirements.systemMessage,
|
|
17357
|
+
knowledgeSources: modelRequirements.knowledgeSources,
|
|
17358
|
+
/*
|
|
17359
|
+
!!!
|
|
17360
|
+
metadata: {
|
|
17361
|
+
agentModelName: this.modelName,
|
|
17362
|
+
}
|
|
17363
|
+
*/
|
|
17364
|
+
});
|
|
17365
|
+
AgentLlmExecutionTools.assistantCache.set(this.title, {
|
|
17366
|
+
assistantId: assistant.assistantId,
|
|
17367
|
+
requirementsHash,
|
|
17368
|
+
});
|
|
17369
|
+
}
|
|
17370
|
+
underlyingLlmResult = await assistant.callChatModelStream(chatPrompt, onProgress);
|
|
16937
17371
|
}
|
|
16938
17372
|
else {
|
|
16939
17373
|
if (this.options.isVerbose) {
|
|
16940
|
-
console.log(`Creating Assistant ${this.title} on generic LLM execution tools...`);
|
|
17374
|
+
console.log(`2ļøā£ Creating Assistant ${this.title} on generic LLM execution tools...`);
|
|
16941
17375
|
}
|
|
16942
17376
|
// Create modified chat prompt with agent system message
|
|
16943
17377
|
const modifiedChatPrompt = {
|
|
@@ -16952,7 +17386,16 @@
|
|
|
16952
17386
|
: ''),
|
|
16953
17387
|
},
|
|
16954
17388
|
};
|
|
16955
|
-
|
|
17389
|
+
if (this.options.llmTools.callChatModelStream) {
|
|
17390
|
+
underlyingLlmResult = await this.options.llmTools.callChatModelStream(modifiedChatPrompt, onProgress);
|
|
17391
|
+
}
|
|
17392
|
+
else if (this.options.llmTools.callChatModel) {
|
|
17393
|
+
underlyingLlmResult = await this.options.llmTools.callChatModel(modifiedChatPrompt);
|
|
17394
|
+
onProgress(underlyingLlmResult);
|
|
17395
|
+
}
|
|
17396
|
+
else {
|
|
17397
|
+
throw new Error('Underlying LLM execution tools do not support chat model calls');
|
|
17398
|
+
}
|
|
16956
17399
|
}
|
|
16957
17400
|
let content = underlyingLlmResult.content;
|
|
16958
17401
|
// Note: Cleanup the AI artifacts from the content
|
|
@@ -16967,6 +17410,10 @@
|
|
|
16967
17410
|
return agentResult;
|
|
16968
17411
|
}
|
|
16969
17412
|
}
|
|
17413
|
+
/**
|
|
17414
|
+
* Cache of OpenAI assistants to avoid creating duplicates
|
|
17415
|
+
*/
|
|
17416
|
+
AgentLlmExecutionTools.assistantCache = new Map();
|
|
16970
17417
|
/**
|
|
16971
17418
|
* TODO: [š] Implement Destroyable pattern to free resources
|
|
16972
17419
|
* TODO: [š§ ] Adding parameter substitution support (here or should be responsibility of the underlying LLM Tools)
|
|
@@ -16975,15 +17422,28 @@
|
|
|
16975
17422
|
/**
|
|
16976
17423
|
* Represents one AI Agent
|
|
16977
17424
|
*
|
|
16978
|
-
*
|
|
17425
|
+
* Note: [š¦] There are several different things in Promptbook:
|
|
16979
17426
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
16980
17427
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
16981
17428
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
16982
17429
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
17430
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
16983
17431
|
*
|
|
16984
17432
|
* @public exported from `@promptbook/core`
|
|
16985
17433
|
*/
|
|
16986
17434
|
class Agent extends AgentLlmExecutionTools {
|
|
17435
|
+
/**
|
|
17436
|
+
* Name of the agent
|
|
17437
|
+
*/
|
|
17438
|
+
get agentName() {
|
|
17439
|
+
return this._agentName || createDefaultAgentName(this.agentSource.value);
|
|
17440
|
+
}
|
|
17441
|
+
/**
|
|
17442
|
+
* Computed hash of the agent source for integrity verification
|
|
17443
|
+
*/
|
|
17444
|
+
get agentHash() {
|
|
17445
|
+
return computeAgentHash(this.agentSource.value);
|
|
17446
|
+
}
|
|
16987
17447
|
/**
|
|
16988
17448
|
* Not used in Agent, always returns empty array
|
|
16989
17449
|
*/
|
|
@@ -16997,34 +17457,40 @@
|
|
|
16997
17457
|
super({
|
|
16998
17458
|
isVerbose: options.isVerbose,
|
|
16999
17459
|
llmTools: getSingleLlmExecutionTools(options.executionTools.llm),
|
|
17000
|
-
agentSource: agentSource.value, // <- TODO:
|
|
17460
|
+
agentSource: agentSource.value, // <- TODO: [š±āš] Allow to pass BehaviorSubject<string_book> OR refresh llmExecutionTools.callChat on agentSource change
|
|
17001
17461
|
});
|
|
17002
|
-
|
|
17003
|
-
* Name of the agent
|
|
17004
|
-
*/
|
|
17005
|
-
this.agentName = null;
|
|
17462
|
+
this._agentName = undefined;
|
|
17006
17463
|
/**
|
|
17007
17464
|
* Description of the agent
|
|
17008
17465
|
*/
|
|
17009
17466
|
this.personaDescription = null;
|
|
17467
|
+
/**
|
|
17468
|
+
* The initial message shown to the user when the chat starts
|
|
17469
|
+
*/
|
|
17470
|
+
this.initialMessage = null;
|
|
17471
|
+
/**
|
|
17472
|
+
* Links found in the agent source
|
|
17473
|
+
*/
|
|
17474
|
+
this.links = [];
|
|
17010
17475
|
/**
|
|
17011
17476
|
* Metadata like image or color
|
|
17012
17477
|
*/
|
|
17013
17478
|
this.meta = {};
|
|
17014
|
-
// TODO:
|
|
17015
|
-
// TODO:
|
|
17479
|
+
// TODO: [š±āš] Add `Agent` simple "mocked" learning by appending to agent source
|
|
17480
|
+
// TODO: [š±āš] Add `Agent` learning by promptbookAgent
|
|
17016
17481
|
this.agentSource = agentSource;
|
|
17017
17482
|
this.agentSource.subscribe((source) => {
|
|
17018
|
-
const { agentName, personaDescription, meta } = parseAgentSource(source);
|
|
17019
|
-
this.
|
|
17483
|
+
const { agentName, personaDescription, initialMessage, links, meta } = parseAgentSource(source);
|
|
17484
|
+
this._agentName = agentName;
|
|
17020
17485
|
this.personaDescription = personaDescription;
|
|
17486
|
+
this.initialMessage = initialMessage;
|
|
17487
|
+
this.links = links;
|
|
17021
17488
|
this.meta = { ...this.meta, ...meta };
|
|
17022
17489
|
});
|
|
17023
17490
|
}
|
|
17024
17491
|
}
|
|
17025
17492
|
/**
|
|
17026
17493
|
* TODO: [š§ ][š°]Agent is not working with the parameters, should it be?
|
|
17027
|
-
* TODO: !!! Agent on remote server
|
|
17028
17494
|
*/
|
|
17029
17495
|
|
|
17030
17496
|
/**
|
|
@@ -17090,24 +17556,24 @@
|
|
|
17090
17556
|
/**
|
|
17091
17557
|
* Represents one AI Agent
|
|
17092
17558
|
*
|
|
17093
|
-
*
|
|
17559
|
+
* Note: [š¦] There are several different things in Promptbook:
|
|
17094
17560
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
17095
|
-
* !!!! `RemoteAgent`
|
|
17096
17561
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
17097
17562
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
17098
17563
|
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
17564
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
17099
17565
|
*
|
|
17100
17566
|
* @public exported from `@promptbook/core`
|
|
17101
17567
|
*/
|
|
17102
17568
|
class RemoteAgent extends Agent {
|
|
17103
17569
|
static async connect(options) {
|
|
17104
|
-
console.log('
|
|
17570
|
+
console.log('[š±āš]', `${options.agentUrl}/api/book`);
|
|
17105
17571
|
const bookResponse = await fetch(`${options.agentUrl}/api/book`);
|
|
17106
|
-
// <- TODO:
|
|
17107
|
-
// <- TODO:
|
|
17572
|
+
// <- TODO: [š±āš] What about closed-source agents?
|
|
17573
|
+
// <- TODO: [š±āš] Maybe use promptbookFetch
|
|
17108
17574
|
const agentSourceValue = (await bookResponse.text());
|
|
17109
17575
|
const agentSource = new rxjs.BehaviorSubject(agentSourceValue);
|
|
17110
|
-
// <- TODO:
|
|
17576
|
+
// <- TODO: [š±āš] Support updating and self-updating
|
|
17111
17577
|
return new RemoteAgent({
|
|
17112
17578
|
...options,
|
|
17113
17579
|
executionTools: {
|
|
@@ -17134,13 +17600,29 @@
|
|
|
17134
17600
|
* Calls the agent on agents remote server
|
|
17135
17601
|
*/
|
|
17136
17602
|
async callChatModel(prompt) {
|
|
17603
|
+
return this.callChatModelStream(prompt, () => { });
|
|
17604
|
+
}
|
|
17605
|
+
/**
|
|
17606
|
+
* Calls the agent on agents remote server with streaming
|
|
17607
|
+
*/
|
|
17608
|
+
async callChatModelStream(prompt, onProgress) {
|
|
17137
17609
|
// Ensure we're working with a chat prompt
|
|
17138
17610
|
if (prompt.modelRequirements.modelVariant !== 'CHAT') {
|
|
17139
17611
|
throw new Error('Agents only supports chat prompts');
|
|
17140
17612
|
}
|
|
17141
|
-
const
|
|
17142
|
-
|
|
17143
|
-
|
|
17613
|
+
const chatPrompt = prompt;
|
|
17614
|
+
const bookResponse = await fetch(`${this.agentUrl}/api/chat`, {
|
|
17615
|
+
method: 'POST',
|
|
17616
|
+
headers: {
|
|
17617
|
+
'Content-Type': 'application/json',
|
|
17618
|
+
},
|
|
17619
|
+
body: JSON.stringify({
|
|
17620
|
+
message: prompt.content,
|
|
17621
|
+
thread: chatPrompt.thread,
|
|
17622
|
+
}),
|
|
17623
|
+
});
|
|
17624
|
+
// <- TODO: [š±āš] What about closed-source agents?
|
|
17625
|
+
// <- TODO: [š±āš] Maybe use promptbookFetch
|
|
17144
17626
|
let content = '';
|
|
17145
17627
|
if (!bookResponse.body) {
|
|
17146
17628
|
content = await bookResponse.text();
|
|
@@ -17159,16 +17641,37 @@
|
|
|
17159
17641
|
const textChunk = decoder.decode(value, { stream: true });
|
|
17160
17642
|
// console.debug('RemoteAgent chunk:', textChunk);
|
|
17161
17643
|
content += textChunk;
|
|
17644
|
+
onProgress({
|
|
17645
|
+
content,
|
|
17646
|
+
modelName: this.modelName,
|
|
17647
|
+
timing: {},
|
|
17648
|
+
usage: {},
|
|
17649
|
+
rawPromptContent: {},
|
|
17650
|
+
rawRequest: {},
|
|
17651
|
+
rawResponse: {},
|
|
17652
|
+
});
|
|
17162
17653
|
}
|
|
17163
17654
|
}
|
|
17164
17655
|
// Flush any remaining decoder internal state
|
|
17165
|
-
|
|
17656
|
+
const lastChunk = decoder.decode();
|
|
17657
|
+
if (lastChunk) {
|
|
17658
|
+
content += lastChunk;
|
|
17659
|
+
onProgress({
|
|
17660
|
+
content: lastChunk,
|
|
17661
|
+
modelName: this.modelName,
|
|
17662
|
+
timing: {},
|
|
17663
|
+
usage: {},
|
|
17664
|
+
rawPromptContent: {},
|
|
17665
|
+
rawRequest: {},
|
|
17666
|
+
rawResponse: {},
|
|
17667
|
+
});
|
|
17668
|
+
}
|
|
17166
17669
|
}
|
|
17167
17670
|
finally {
|
|
17168
17671
|
reader.releaseLock();
|
|
17169
17672
|
}
|
|
17170
17673
|
}
|
|
17171
|
-
// <- TODO:
|
|
17674
|
+
// <- TODO: [š±āš] Transfer metadata
|
|
17172
17675
|
const agentResult = {
|
|
17173
17676
|
content,
|
|
17174
17677
|
modelName: this.modelName,
|
|
@@ -17177,7 +17680,7 @@
|
|
|
17177
17680
|
rawPromptContent: {},
|
|
17178
17681
|
rawRequest: {},
|
|
17179
17682
|
rawResponse: {},
|
|
17180
|
-
// <- TODO:
|
|
17683
|
+
// <- TODO: [š±āš] Transfer and proxy the metadata
|
|
17181
17684
|
};
|
|
17182
17685
|
return agentResult;
|
|
17183
17686
|
}
|
|
@@ -17308,24 +17811,6 @@
|
|
|
17308
17811
|
* Note: [š] Ignore a discrepancy between file name and entity name
|
|
17309
17812
|
*/
|
|
17310
17813
|
|
|
17311
|
-
/**
|
|
17312
|
-
* Detects if the code is running in jest environment
|
|
17313
|
-
*
|
|
17314
|
-
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environment
|
|
17315
|
-
*
|
|
17316
|
-
* @public exported from `@promptbook/utils`
|
|
17317
|
-
*/
|
|
17318
|
-
const $isRunningInJest = new Function(`
|
|
17319
|
-
try {
|
|
17320
|
-
return process.env.JEST_WORKER_ID !== undefined;
|
|
17321
|
-
} catch (e) {
|
|
17322
|
-
return false;
|
|
17323
|
-
}
|
|
17324
|
-
`);
|
|
17325
|
-
/**
|
|
17326
|
-
* TODO: [šŗ]
|
|
17327
|
-
*/
|
|
17328
|
-
|
|
17329
17814
|
/**
|
|
17330
17815
|
* Registration of LLM provider metadata
|
|
17331
17816
|
*
|
|
@@ -17678,61 +18163,6 @@
|
|
|
17678
18163
|
* TODO: [š§ ][š“] Where is the best location for this file
|
|
17679
18164
|
*/
|
|
17680
18165
|
|
|
17681
|
-
/**
|
|
17682
|
-
* Tag function for notating a prompt as template literal
|
|
17683
|
-
*
|
|
17684
|
-
* Note: There are 3 similar functions:
|
|
17685
|
-
* 1) `prompt` for notating single prompt exported from `@promptbook/utils`
|
|
17686
|
-
* 2) `promptTemplate` alias for `prompt`
|
|
17687
|
-
* 3) `book` for notating and validating entire books exported from `@promptbook/utils`
|
|
17688
|
-
*
|
|
17689
|
-
* @param strings
|
|
17690
|
-
* @param values
|
|
17691
|
-
* @returns the prompt string
|
|
17692
|
-
* @public exported from `@promptbook/utils`
|
|
17693
|
-
*/
|
|
17694
|
-
function prompt(strings, ...values) {
|
|
17695
|
-
if (values.length === 0) {
|
|
17696
|
-
return spaceTrim__default["default"](strings.join(''));
|
|
17697
|
-
}
|
|
17698
|
-
const stringsWithHiddenParameters = strings.map((stringsItem) =>
|
|
17699
|
-
// TODO: [0] DRY
|
|
17700
|
-
stringsItem.split('{').join(`${REPLACING_NONCE}beginbracket`).split('}').join(`${REPLACING_NONCE}endbracket`));
|
|
17701
|
-
const placeholderParameterNames = values.map((value, i) => `${REPLACING_NONCE}${i}`);
|
|
17702
|
-
const parameters = Object.fromEntries(values.map((value, i) => [placeholderParameterNames[i], value]));
|
|
17703
|
-
// Combine strings and values
|
|
17704
|
-
let pipelineString = stringsWithHiddenParameters.reduce((result, stringsItem, i) => placeholderParameterNames[i] === undefined
|
|
17705
|
-
? `${result}${stringsItem}`
|
|
17706
|
-
: `${result}${stringsItem}{${placeholderParameterNames[i]}}`, '');
|
|
17707
|
-
pipelineString = spaceTrim__default["default"](pipelineString);
|
|
17708
|
-
try {
|
|
17709
|
-
pipelineString = templateParameters(pipelineString, parameters);
|
|
17710
|
-
}
|
|
17711
|
-
catch (error) {
|
|
17712
|
-
if (!(error instanceof PipelineExecutionError)) {
|
|
17713
|
-
throw error;
|
|
17714
|
-
}
|
|
17715
|
-
console.error({ pipelineString, parameters, placeholderParameterNames, error });
|
|
17716
|
-
throw new UnexpectedError(spaceTrim__default["default"]((block) => `
|
|
17717
|
-
Internal error in prompt template literal
|
|
17718
|
-
|
|
17719
|
-
${block(JSON.stringify({ strings, values }, null, 4))}}
|
|
17720
|
-
|
|
17721
|
-
`));
|
|
17722
|
-
}
|
|
17723
|
-
// TODO: [0] DRY
|
|
17724
|
-
pipelineString = pipelineString
|
|
17725
|
-
.split(`${REPLACING_NONCE}beginbracket`)
|
|
17726
|
-
.join('{')
|
|
17727
|
-
.split(`${REPLACING_NONCE}endbracket`)
|
|
17728
|
-
.join('}');
|
|
17729
|
-
return pipelineString;
|
|
17730
|
-
}
|
|
17731
|
-
/**
|
|
17732
|
-
* TODO: [š§ ][š“] Where is the best location for this file
|
|
17733
|
-
* Note: [š] Ignore a discrepancy between file name and entity name
|
|
17734
|
-
*/
|
|
17735
|
-
|
|
17736
18166
|
/**
|
|
17737
18167
|
* Tag function for notating a pipeline with a book\`...\ notation as template literal
|
|
17738
18168
|
*
|
|
@@ -18268,7 +18698,7 @@
|
|
|
18268
18698
|
});
|
|
18269
18699
|
|
|
18270
18700
|
const answer = response.choices[0].message.content;
|
|
18271
|
-
console.log('\\nš§ ${agentName}:', answer, '\\n');
|
|
18701
|
+
console.log('\\nš§ ${agentName /* <- TODO: [š] There should be `agentFullname` not `agentName` */}:', answer, '\\n');
|
|
18272
18702
|
|
|
18273
18703
|
chatHistory.push({ role: 'assistant', content: answer });
|
|
18274
18704
|
promptUser();
|
|
@@ -18287,7 +18717,7 @@
|
|
|
18287
18717
|
|
|
18288
18718
|
(async () => {
|
|
18289
18719
|
await setupKnowledge();
|
|
18290
|
-
console.log("š¤ Chat with ${agentName} (type 'exit' to quit)\\n");
|
|
18720
|
+
console.log("š¤ Chat with ${agentName /* <- TODO: [š] There should be `agentFullname` not `agentName` */} (type 'exit' to quit)\\n");
|
|
18291
18721
|
promptUser();
|
|
18292
18722
|
})();
|
|
18293
18723
|
`);
|
|
@@ -18334,7 +18764,7 @@
|
|
|
18334
18764
|
});
|
|
18335
18765
|
|
|
18336
18766
|
const answer = response.choices[0].message.content;
|
|
18337
|
-
console.log('\\nš§ ${agentName}:', answer, '\\n');
|
|
18767
|
+
console.log('\\nš§ ${agentName /* <- TODO: [š] There should be `agentFullname` not `agentName` */}:', answer, '\\n');
|
|
18338
18768
|
|
|
18339
18769
|
chatHistory.push({ role: 'assistant', content: answer });
|
|
18340
18770
|
promptUser();
|
|
@@ -18351,7 +18781,7 @@
|
|
|
18351
18781
|
});
|
|
18352
18782
|
}
|
|
18353
18783
|
|
|
18354
|
-
console.log("š¤ Chat with ${agentName} (type 'exit' to quit)\\n");
|
|
18784
|
+
console.log("š¤ Chat with ${agentName /* <- TODO: [š] There should be `agentFullname` not `agentName` */} (type 'exit' to quit)\\n");
|
|
18355
18785
|
promptUser();
|
|
18356
18786
|
|
|
18357
18787
|
`);
|
|
@@ -18359,25 +18789,6 @@
|
|
|
18359
18789
|
},
|
|
18360
18790
|
};
|
|
18361
18791
|
|
|
18362
|
-
/**
|
|
18363
|
-
* Returns information about the current runtime environment
|
|
18364
|
-
*
|
|
18365
|
-
* Note: `$` is used to indicate that this function is not a pure function - it looks at the global object to determine the environments
|
|
18366
|
-
*
|
|
18367
|
-
* @public exported from `@promptbook/utils`
|
|
18368
|
-
*/
|
|
18369
|
-
function $detectRuntimeEnvironment() {
|
|
18370
|
-
return {
|
|
18371
|
-
isRunningInBrowser: $isRunningInBrowser(),
|
|
18372
|
-
isRunningInJest: $isRunningInJest(),
|
|
18373
|
-
isRunningInNode: $isRunningInNode(),
|
|
18374
|
-
isRunningInWebWorker: $isRunningInWebWorker(),
|
|
18375
|
-
};
|
|
18376
|
-
}
|
|
18377
|
-
/**
|
|
18378
|
-
* TODO: [šŗ] Also detect and report node version here
|
|
18379
|
-
*/
|
|
18380
|
-
|
|
18381
18792
|
/**
|
|
18382
18793
|
* Provide information about Promptbook, engine version, book language version, servers, ...
|
|
18383
18794
|
*
|
|
@@ -18556,7 +18967,7 @@
|
|
|
18556
18967
|
const agentSource = validateBook(spaceTrim__default["default"]((block) => `
|
|
18557
18968
|
${agentName}
|
|
18558
18969
|
|
|
18559
|
-
META COLOR ${color || '#3498db' /* <- TODO:
|
|
18970
|
+
META COLOR ${color || '#3498db' /* <- TODO: [š§ ] [š±āš] Best default color */}
|
|
18560
18971
|
PERSONA ${block(personaDescription)}
|
|
18561
18972
|
`));
|
|
18562
18973
|
return agentSource;
|
|
@@ -18699,12 +19110,14 @@
|
|
|
18699
19110
|
exports.book = book;
|
|
18700
19111
|
exports.cacheLlmTools = cacheLlmTools;
|
|
18701
19112
|
exports.compilePipeline = compilePipeline;
|
|
19113
|
+
exports.computeAgentHash = computeAgentHash;
|
|
18702
19114
|
exports.computeCosineSimilarity = computeCosineSimilarity;
|
|
18703
19115
|
exports.countUsage = countUsage;
|
|
18704
19116
|
exports.createAgentLlmExecutionTools = createAgentLlmExecutionTools;
|
|
18705
19117
|
exports.createAgentModelRequirements = createAgentModelRequirements;
|
|
18706
19118
|
exports.createAgentModelRequirementsWithCommitments = createAgentModelRequirementsWithCommitments;
|
|
18707
19119
|
exports.createBasicAgentModelRequirements = createBasicAgentModelRequirements;
|
|
19120
|
+
exports.createDefaultAgentName = createDefaultAgentName;
|
|
18708
19121
|
exports.createEmptyAgentModelRequirements = createEmptyAgentModelRequirements;
|
|
18709
19122
|
exports.createLlmToolsFromConfiguration = createLlmToolsFromConfiguration;
|
|
18710
19123
|
exports.createPipelineCollectionFromJson = createPipelineCollectionFromJson;
|
|
@@ -18734,6 +19147,7 @@
|
|
|
18734
19147
|
exports.limitTotalUsage = limitTotalUsage;
|
|
18735
19148
|
exports.makeKnowledgeSourceHandler = makeKnowledgeSourceHandler;
|
|
18736
19149
|
exports.migratePipeline = migratePipeline;
|
|
19150
|
+
exports.normalizeAgentName = normalizeAgentName;
|
|
18737
19151
|
exports.padBook = padBook;
|
|
18738
19152
|
exports.parseAgentSource = parseAgentSource;
|
|
18739
19153
|
exports.parseParameters = parseParameters;
|