@convo-lang/convo-lang
Version:
The language of AI
1,528 lines (1,527 loc) • 84.2 kB
JavaScript
import { UnsupportedError, asArray, deepClone, dupDeleteUndefined, getObjKeyCount, getValueByPath, parseBoolean, parseRegexCached, starStringTestCached, zodTypeToJsonScheme } from "@iyio/common";
import { parseJson5 } from '@iyio/json5';
import { format } from "date-fns";
import { ConvoError } from "./ConvoError.js";
import { convoSystemMessages } from "./convo-system-messages.js";
import { convoFlowControllerKey, convoObjFlag, convoScopeFunctionMarker } from "./convo-types.js";
export const convoBodyFnName = '__body';
export const convoArgsName = '__args';
export const convoResultReturnName = '__return';
export const convoResultErrorName = '__error';
export const convoDisableAutoCompleteName = '__disableAutoComplete';
export const convoStructFnName = 'struct';
export const convoNewFnName = 'new';
export const convoMapFnName = 'map';
export const convoArrayFnName = 'array';
export const convoJsonMapFnName = 'jsonMap';
export const convoJsonArrayFnName = 'jsonArray';
export const convoSwitchFnName = 'switch';
export const convoCaseFnName = 'case';
export const convoDefaultFnName = 'default';
export const convoTestFnName = 'test';
export const convoPipeFnName = 'pipe';
export const convoLocalFunctionModifier = 'local';
export const convoExternFunctionModifier = 'extern';
export const convoCallFunctionModifier = 'call';
export const convoInvokeFunctionModifier = 'invoke';
export const convoInvokeFunctionName = 'invoke';
export const convoGlobalRef = 'convo';
export const convoEnumFnName = 'enum';
export const convoMetadataKey = Symbol('convoMetadataKey');
export const convoCaptureMetadataTag = 'captureMetadata';
export const defaultConversationName = 'default';
export const convoLogDir = '.convo-lang-logs';
export const convoMsgModifiers = {
/**
* When applied to the function the function is used as the default function of an agent
*/
agent: 'agent',
};
export const convoScopedModifiers = [convoMsgModifiers.agent];
export const defaultConvoTask = 'default';
export const convoAnyModelName = '__any__';
export const convoRagTemplatePlaceholder = '$$RAG$$';
export const convoRoles = {
user: 'user',
assistant: 'assistant',
system: 'system',
/**
* Used to add a prefix to the previous content message. Prefixes are not seen by the user.
*/
prefix: 'prefix',
/**
* Used to add a suffix to the previous content message. Suffixes are not seen by the user.
*/
suffix: 'suffix',
/**
* Appends content to the previous content message
*/
append: 'append',
/**
* Appends content to the last system message
*/
appendSystem: 'appendSystem',
/**
* Appends content to the last user message
*/
appendUser: 'appendUser',
/**
* Appends content to the last assistant message
*/
appendAssistant: 'appendAssistant',
/**
* Prepends content to the previous content message
*/
prepend: 'prepend',
/**
* Used to replace the content of the previous content message
*/
replace: 'replace',
/**
* Used to replace the content of the previous content message before sending to an LLM. The
* user will continue to see the previous content message.
*/
replaceForModel: 'replaceForModel',
/**
* Used to display message evaluated by inline / thinking prompts
*/
thinking: 'thinking',
/**
* Used to set variables set within inline / thinking prompts
*/
thinkingResult: 'thinkingResult',
/**
* Contains RAG content
*/
rag: 'rag',
/**
* Used to define a prefix to add to rag messages
*/
ragPrefix: 'ragPrefix',
/**
* Used to define a suffix to add to rag messages
*/
ragSuffix: 'ragSuffix',
/**
* A message used as a template to insert RAG content into. The value $$RAG$$ will be used replaced with the actual rag content
*/
ragTemplate: 'ragTemplate',
/**
* importTemplate messages are used to format imported content such as markdown.
*/
importTemplate: 'importTemplate',
/**
* When encountered a conversation will executed the preceding message before continuing unless
* preceded by a flushed message.
*/
queue: 'queue',
/**
* signals a queue has been flushed
*/
flush: 'flush',
/**
* Starts an insertion block. Insertion blocks are used to reorder messages in a flattened conversation.
*/
insert: 'insert',
/**
* Ends an insertion block.
*/
insertEnd: 'insertEnd',
/**
* No op role. Messages with this role are completely ignored
*/
nop: 'nop',
/**
* Used to track transform results including tokens used by transforms
*/
transformResult: 'transformResult',
/**
* Starts a parallel execution block
*/
parallel: 'parallel',
/**
* Ends a parallel execution block
*/
parallelEnd: 'parallelEnd',
/**
* Ends the definition of an agent
*/
agentEnd: 'agentEnd',
call: 'call',
do: 'do',
result: 'result',
define: 'define',
debug: 'debug',
end: 'end',
/**
* Used by the convo make build system to define a make target. `std://make.convo` must
* be imported to function correctly
*/
target: 'target',
/**
* Used by the convo make build system to define target defaults and configure build options
*/
make: 'make',
/**
* Used by the convo make build system to define an app. `std://make.convo` must
* be imported to function correctly
*/
app: 'app',
/**
* Used by the convo make build system to define a build stage. `std://make.convo` must
* be imported to function correctly
*/
stage: 'stage',
};
/**
* List of built-in role that are allowed to be used with custom message handlers
*/
export const convoHandlerAllowedRoles = [
convoRoles.make,
convoRoles.target,
convoRoles.app,
convoRoles.stage,
];
/**
* Reserved role names in Convo-Lang that have special meaning and cannot be used as custom roles.
* These roles are used for system functionality like function calls, execution blocks, and debugging.
*/
export const convoReservedRoles = [
convoRoles.call,
convoRoles.do,
convoRoles.result,
convoRoles.define,
convoRoles.debug,
convoRoles.end,
convoRoles.thinking
];
export const convoFunctions = {
queryImage: 'queryImage',
getState: 'getState',
/**
* When called __rag with be set to true and and params passed will be added the the __ragParams
* array. If __ragParams is not an array it will be set to an array first. Duplicated values
* will not be added to __ragParams.
*/
enableRag: 'enableRag',
/**
* Disables and clears all rag params
*/
clearRag: 'clearRag',
/**
* Defines a form that a user can be guided through
*/
defineForm: 'defineForm',
today: 'today',
uuid: 'uuid',
shortUuid: 'shortUuid',
getVar: 'getVar',
setVar: 'setVar',
idx: 'idx',
describeScene: 'describeScene',
readDoc: 'readDoc',
/**
* States the default value of a variable.
*/
setDefault: 'setDefault',
/**
* Returns an XML list of agents available to the current conversation.
*/
getAgentList: 'getAgentList',
/**
* Explicitly enables a transform by name
*/
enableTransform: 'enableTransform',
/**
* Explicitly enables all transforms
*/
enableAllTransforms: 'enableAllTransforms',
/**
* Pushes a conversation task on to the task stack. The task will be display in the UI
* while a completion is in progress
*/
pushConvoTask: 'pushConvoTask',
/**
* Pops the last convo task off the stack
*/
popConvoTask: 'popConvoTask',
/**
* Reads a JSON value from the virtual file system
*/
fsReadJson: 'fsReadJson',
/**
* Reads a file from the virtual file system and returns it as a base64 string
*/
fsReadBase64: 'fsReadBase64',
/**
* Reads a file from the virtual file system and returns it as a base64 url. This function can
* be used to read files as images or other assets that get embedded as base 64 urls.
*
* @signature (path:string contentType?:string) -> string
*
* @example This is an image: }})
*/
fsReadBase64Url: 'fsReadBase64Url',
/**
* Similar to fsReadBase64Url but returns an image formatted as markdown
*
* @signature (path:string description?:string contentType?:string) -> string
*/
fsReadBase64Image: 'fsReadBase64Image',
/**
* Creates a markdown image from the given path and description. mdImg is an alias of `fsReadBase64Image`
*
* @signature (path:string description?:string contentType?:string) -> string
*/
mdImg: 'mdImg',
/**
* Writes a JSON value to the virtual file system and returns the written value.
*/
fsWriteJson: 'fsWriteJson',
/**
* Reads a string value from the virtual file system
*/
fsRead: 'fsRead',
/**
* Reads multiple files as a single string using a list of names and an optional pattern that
* the each name is inserted into.
*/
fsMultiRead: 'fsMultiRead',
/**
* Writes a string value to the virtual file system and returns the written value.
*/
fsWrite: 'fsWrite',
/**
* Delete a file or directory from the virtual file system
*/
fsRemove: 'fsRemove',
/**
* Creates a directory in the virtual file system
*/
fsMkDir: 'fsMkDir',
/**
* Checks if a path exists in the virtual file system
*/
fsExists: 'fsExists',
/**
* Reads directory items
*/
fsReadDir: 'fsReadDir',
/**
* Returns the full and normalized path for the given value.
*/
fsFullPath: 'fsFullPath',
/**
* Joins file paths
*/
joinPaths: 'joinPaths',
/**
* Returns true if all values passed to the function are undefined
*/
isUndefined: 'isUndefined',
/**
* Returns the passed in value as milliseconds
*/
secondMs: 'secondMs',
/**
* Returns the passed in value as milliseconds
*/
minuteMs: 'minuteMs',
/**
* Returns the passed in value as milliseconds
*/
hourMs: 'hourMs',
/**
* Returns the passed in value as milliseconds
*/
dayMs: 'dayMs',
/**
* Finds an item in an array using shallow comparison.
*/
aryFindMatch: 'aryFindMatch',
/**
* Removes the first matching item in an array using shallow comparison.
*/
aryRemoveMatch: 'aryRemoveMatch',
/**
* Used by the convo make build system to define target defaults and build options
*/
makeDefaults: 'makeDefaults',
/**
* Used by the convo make build system to define a output to make
*/
makeTarget: 'makeTarget',
/**
* Defines a make app
*/
makeApp: 'makeApp',
/**
* Defines a make stage
*/
makeStage: 'makeStage',
/**
* Similar to the map function except unlabeled values are place the the `_` property
*/
mapWithCapture: 'mapWithCapture',
/**
* Used to access properties of a make target by path
*/
mkt: 'mkt',
};
/**
* reserved system variables
*/
export const convoVars = {
[convoResultReturnName]: convoResultReturnName,
/**
* Used to enabled prompt caching. A value of true will use the default prompt cached which
* by default uses the `ConvoLocalStorageCache`. If assigned a string a cache with a matching
* type will be used.
*/
__cache: '__cache',
/**
* In environments that have access to the filesystem __cwd defines the current working directory.
*/
__cwd: '__cwd',
/**
* Path to the main file that loaded the conversation
*/
__mainFile: '__mainFile',
/**
* Path to the current convo file
*/
__file: '__file',
/**
* When set to true debugging information will be added to conversations.
*/
__debug: '__debug',
/**
* Sets the default model
*/
__model: '__model',
/**
* Sets the default completion endpoint
*/
__endpoint: '__endpoint',
/**
* Endpoint to a convo compatible endpoint
*/
__convoEndpoint: '__convoEndpoint',
/**
* API key to send to completions endpoint. The `apiKey` of the `FlatConvoConversationBase` will
* be populated by this variable if defined.
*/
__apiKey: '__apiKey',
/**
* Sets the default user id of the conversation
*/
__userId: '__userId',
/**
* When set to true time tracking will be enabled.
*/
__trackTime: '__trackTime',
/**
* When set to true token usage tracking will be enabled.
*/
__trackTokenUsage: '__trackTokenUsage',
/**
* When set to true the model used as a completion provider will be tracked.
*/
__trackModel: '__trackModel',
/**
* When defined __visionSystemMessage will be injected into the system message of conversations
* with vision capabilities. __visionSystemMessage will override the default vision
* system message.
*/
__visionSystemMessage: '__visionSystemMessage',
/**
* The default system message used for completing vision requests. Vision requests are typically
* completed in a separate conversation that supports vision messages. By default the system
* message of the conversation that triggered the vision request will be used.
*/
__visionServiceSystemMessage: '__visionServiceSystemMessage',
/**
* Response used with the system is not able to generate a vision response.
*/
__defaultVisionResponse: '__defaultVisionResponse',
/**
* A reference to markdown vars.
*/
__md: '__md',
/**
* Enables retrieval augmented generation (RAG). The value of the __rag can either be true,
* false or a number. The value indicates the number of rag results that should be sent to the
* LLM by default all rag message will be sent to the LLM. When setting the number of rag
* messages to a fixed number only the last N number of rag messages will be sent to the LLM.
* Setting __rag to a fixed number can help to reduce prompt size.
*/
__rag: '__rag',
/**
* An object that will be passed to the rag callback of a conversation. If the value is not an
* object it is ignored.
*/
__ragParams: '__ragParams',
/**
* The tolerance that determines if matched rag content should be included as contact.
*/
__ragTol: '__ragTol',
/**
* Sets the current thread filter. Can either be a string or a ConvoThreadFilter. If __threadFilter
* is a string it will be converted into a filter that looks like `{includeThreads:[__threadId]}`.
*/
__threadFilter: '__threadFilter',
/**
* A reference to a SceneCtrl that is capable of describing the current scene the user is viewing.
*/
__sceneCtrl: '__sceneCtrl',
/**
* The last described scene added to the conversation
*/
__lastDescribedScene: '__lastDescribedScene',
/**
* Used by agents to define the voice they use
*/
__voice: '__voice',
/**
* used to indicate that forms have been enabled
*/
__formsEnabled: '__formsEnabled',
/**
* Default array of forms
*/
__forms: '__forms',
/**
* Array of transforms names that have explicity been enabled. Transforms are enabled by default
* unless they have the `transformOptional` tag applied. Adding "all" to the list will explicity
* enable all components.
*/
__explicitlyEnabledTransforms: '__explicitlyEnabledTransforms',
/**
* Name of the currently executing trigger
*/
__trigger: '__trigger',
/**
* If true inline prompt messages should be written to debug output
*/
__debugInline: '__debugInline',
/**
* Controls the penalty for repeated tokens in completions.
*/
__frequencyPenalty: '__frequencyPenalty',
/**
* Controls the penalty for new tokens based on their presence in the text so far.
*/
__presencePenalty: '__presencePenalty',
/**
* If set, requests log probabilities of generated tokens.
*/
__logprobs: '__logprobs',
/**
* Sets the maximum number of tokens to generate in a completion.
*/
__maxTokens: '__maxTokens',
/**
* Indicates the level of reasoning effort to apply.
*/
__reasoningEffort: '__reasoningEffort',
/**
* Sets the random seed for reproducible completions.
*/
__seed: '__seed',
/**
* Specifies the service tier to use for completions.
*/
__serviceTier: '__serviceTier',
/**
* Controls the randomness of completions (temperature parameter).
*/
__temperature: '__temperature',
/**
* Controls the nucleus sampling parameter for completions (top_p).
*/
__topP: '__topP',
/**
* Requests the log probabilities for the top tokens.
*/
__topLogprobs: '__topLogprobs',
/**
* Controls the verbosity of the model's response.
*/
__responseVerbosity: '__responseVerbosity',
/**
* Allows biasing the likelihood of specific tokens.
*/
__logitBias: '__logitBias',
/**
* Object containing additional parameters to pass to the LLM.
*/
__modelParams: '__modelParams',
/**
* Array of ConvoMakeApp objects
*/
__makeDefaults: '__makeDefaults',
/**
* Array of ConvoMakeTargetDeclaration objects
*/
__makeTargets: '__makeTargets',
/**
* Array of ConvoMakeApp objects
*/
__makeApps: '__makeApps',
/**
* Array of ConvoMakeStage objects
*/
__makeStages: '__makeStages',
/**
* Relative path to the make directory the current convo file was created in
*/
__makeRoot: '__makeRoot',
__makeFile: '__makeFile',
__makeOut: '__makeOut',
/**
* Maps custom messages to handler functions
*/
__messageHandlers: '__messageHandlers',
/**
* Name of a type to be used as the default json response type
*/
__defaultResponseType: '__defaultResponseType',
};
export const convoImportModifiers = {
/**
* Only system messages should be imported
*/
system: 'system',
/**
* Content messages should be ignored
*/
ignoreContent: 'ignoreContent',
/**
* Merges the contents of multiple files into a single imports. This is useful when importing
* multiple content files using a glob.
*/
merge: 'merge'
};
export const defaultConvoRagTol = 1.2;
export const convoEvents = {
/**
* Occurs when a user message is added to a conversation
*
* Functions listening to the `user` event will be called after user messages are
* appended. The return value of the function will either replaces the content of the user
* message or will be set as the messages prefix or suffix. If the function return false, null or
* undefined it is ignored and the next function listening to the `user` event will be called.
*
* @usage (@)on user [replace|append|prepend|prefix|suffix] [condition]
*/
user: 'user',
/**
* Occurs when an assistant message is added to a conversation.
*
* Functions listening to the `assistant` event will be called after assistant messages are
* appended. The return value of the function will either replaces the content of the assistant
* message or will be set as the messages prefix or suffix. If the function return false, null or
* undefined it is ignored and the next function listening to the `assistant` event will be called.
*
* @usage (@)on assistant [replace|append|prepend|prefix|suffix] [condition]
*/
assistant: 'assistant',
};
export const convoTags = {
/**
* When applied to a user message and the message is the last message in a conversation the message
* is considered a conversation initializer.
*/
init: 'init',
/**
* Used to set the name of the message
*/
name: 'name',
/**
* Defines an event listener for a message. Functions tagged with `@on` will
* be made local and not visible to LLMs.
*/
on: 'on',
/**
* Enable rag for a message. The value of the tag will be added as a rag path
*/
ragForMsg: 'ragForMsg',
/**
* Enables rag for the current conversation
*/
rag: 'rag',
/**
* Defines the start index and length of the actual rag content without prefix and suffix
*/
ragContentRage: 'ragContentRage',
/**
* Manually labels a message
*/
label: 'label',
/**
* Clears all content messages that precede the messages with the exception of system messages.
* If the value of "system" is given as the tags value system message will also be cleared.
*/
clear: 'clear',
/**
* Prevents a message from being clear when followed by a message with a `@clear` tag applied.
*/
noClear: 'noClear',
/**
* Enables caching for the message the tag is applied to. No value of a value of true will use
* the default prompt cached which by default uses the `ConvoLocalStorageCache`. If assigned
* a string a cache with a matching type will be used.
*/
cache: 'cache',
/**
* Controls the penalty for repeated tokens in completions for the message the tag is applied to.
*/
frequencyPenalty: 'frequencyPenalty',
/**
* Controls the penalty for new tokens based on their presence in the text so far for the message the tag is applied to.
*/
presencePenalty: 'presencePenalty',
/**
* If set, requests log probabilities of generated tokens for the message the tag is applied to.
*/
logprobs: 'logprobs',
/**
* Sets the maximum number of tokens to generate in a completion for the message the tag is applied to.
*/
maxTokens: 'maxTokens',
/**
* Indicates the level of reasoning effort to apply for the message the tag is applied to.
*/
reasoningEffort: 'reasoningEffort',
/**
* Sets the random seed for reproducible completions for the message the tag is applied to.
*/
seed: 'seed',
/**
* Specifies the service tier to use for completions for the message the tag is applied to.
*/
serviceTier: 'serviceTier',
/**
* Controls the randomness of completions (temperature parameter) for the message the tag is applied to.
*/
temperature: 'temperature',
/**
* Controls the nucleus sampling parameter for completions (top_p) for the message the tag is applied to.
*/
topP: 'topP',
/**
* Requests the log probabilities for the top tokens for the message the tag is applied to.
*/
topLogprobs: 'topLogprobs',
/**
* Controls the verbosity of the model's response for the message the tag is applied to.
*/
responseVerbosity: 'responseVerbosity',
/**
* Object containing additional parameters to pass to the LLM for the message the tag is applied to.
*/
modelParams: 'modelParams',
/**
* When applied to a function the return value of the function will not be used to generate a
* new assistant message.
*/
disableAutoComplete: 'disableAutoComplete',
/**
* Disables triggers on the message the tag is applied to.
*/
disableTriggers: 'disableTriggers',
/**
* Forces a message to be included in triggers. If the tag defines a value the value will be used
* to match which trigger the message is included in.
*/
includeInTriggers: 'includeInTriggers',
/**
* Excludes a message from being included in triggers. If the tag defines a value the value will
* be used to match the trigger it is excluded from.
*/
excludeFromTriggers: 'excludeFromTriggers',
/**
* When applied to a content message the message will be appended to the conversation after calling the
* function specified by the tag's value. When applied to a function message the content of the
* tag will be appended as a user message.
*/
afterCall: 'afterCall',
/**
* When used with the `afterCall` tag the appended message will be hidden from the user but
* visible to the LLM
*/
afterCallHide: 'afterCallHide',
/**
* When used with the `afterCall` tag the appended message will use the given role
*/
afterCallRole: 'afterCallRole',
/**
* Indicates a message was created by a afterCall tag
*/
createdAfterCalling: 'createdAfterCalling',
/**
* Used to indicate that a message should be evaluated at the edge of a conversation with the
* latest state. (@)edge is most commonly used with system message to ensure that all injected values
* are updated with the latest state of the conversation.
*/
edge: 'edge',
/**
* Used to track the time messages are created.
*/
time: 'time',
/**
* Used to track the number of tokens a message used
*/
tokenUsage: 'tokenUsage',
/**
* Used to track the model used to generate completions
*/
model: 'model',
/**
* Sets the requested model to complete a message with
*/
responseModel: 'responseModel',
/**
* Used to track the endpoint to generate completions
*/
endpoint: 'endpoint',
/**
* Sets the requested endpoint to complete a message with
*/
responseEndpoint: 'responseEndpoint',
/**
* Sets the format as message should be responded to with.
*/
responseFormat: 'responseFormat',
/**
* Causes the response of the tagged message to be assigned to a variable
*/
assign: 'assign',
/**
* When used with a message the json tag is short and for `@responseFormat json`
*/
json: 'json',
/**
* The format of a message
*/
format: 'format',
/**
* Used to assign the content or jsonValue of a message to a variable
*/
assignTo: 'assignTo',
/**
* Used to enable capabilities. Only the first and last message in the conversation are used
* to determine current capabilities. Multiple capability tags can be
* applied to a message and multiple capabilities can be specified by separating them with a
* comma.
*/
capability: 'capability',
/**
* Shorthand for `@capability vision`
* Enables vision for all messages in a conversation
*/
enableVision: 'enableVision',
/**
* Shorthand for `@capability visionFunction`
* The visionFunction capability adds vision support by passing vision messages to a vision model
* and exposing vision capabilities as a function.
*/
enabledVisionFunction: 'enabledVisionFunction',
/**
* Enables vision for the message the tag is applied to
*/
vision: 'vision',
/**
* Sets the task a message is part of. By default messages are part of the "default" task
*/
task: 'task',
/**
* Can be used by functions to display a task message while the function is executing.
*/
taskName: 'taskName',
/**
* Can be used by functions to display a task message while the function is executing.
*/
taskDescription: 'taskDescription',
/**
* Sets the max number of non-system messages that should be included in a task completion
*/
maxTaskMessageCount: 'maxTaskMessageCount',
/**
* Defines what triggers a task
*/
taskTrigger: 'taskTrigger',
/**
* Defines a message as a template
*/
template: 'template',
/**
* used to track the name of templates used to generate messages
*/
sourceTemplate: 'sourceTemplate',
/**
* Used to mark a message as a component. The value can be "render" or "input". The default
* value is "render" if no value is given. When the "input" value is used the rendered component
* will take input from a user then write the input received to the executing conversation.
*/
component: 'component',
/**
* When applied to a message the message should be rendered but not sent to LLMs
*/
renderOnly: 'renderOnly',
/**
* Controls where a message is rendered. By default messages are rendered in the default chat
* view, but applications can define different render targets.
*/
renderTarget: 'renderTarget',
/**
* Sets the renderTarget of the message to "hidden"
*/
hidden: 'hidden',
toolId: 'toolId',
/**
* When applied to the last content or component messages auto scrolling will be disabled
*/
disableAutoScroll: 'disableAutoScroll',
/**
* When applied to a message the content of the message will be parsed as markdown
*/
markdown: 'markdown',
/**
* When applied to a message the content of the message will be parsed as markdown and the
* elements of the markdown will be auto assigned to vars
*/
markdownVars: 'markdownVars',
/**
* When applied to a message the message is conditionally added to the flattened view of a
* conversation. When the condition is false the message will not be visible to the user
* or the LLM.
*
* @note The example below uses (@) instead of the at symbol because of a limitation of jsdoc.
*
* The example below will only render and send the second system message to the LLM
* @example
*
* ``` convo
* > define
* animal = 'dog'
*
* (@)condition animal frog
* > system
* You are a frog and you like to hop around.
*
* (@)condition animal dog
* > system
* You are a dog and you like to eat dirt.
* ```
*/
condition: 'condition',
/**
* When applied to a message the message is completely disregarded and removed from the conversation
*/
disabled: 'disabled',
/**
* Reference to a document related to a message. Typically used with RAG.
*/
docRef: 'docRef',
/**
* When applied to a message the message becomes a clickable suggestion that when clicked will
* add a new user message with the content of the message. If the suggestion tag defines a value
* that value will be displayed on the clickable button instead of the message content but the
* message content will still be used as the user messaged added to the conversation when clicked.
* Suggestion message are render only and not seen by LLMs.
*/
suggestion: 'suggestion',
/**
* A title display above a group of suggestions
*/
suggestionTitle: 'suggestionTitle',
/**
* Sets the threadId of the current message and all following messages. Using the `@thread` tag
* without a value will clear the current thread id.
*/
thread: 'thread',
/**
* Used to mark a function as a node output.
*/
output: 'output',
/**
* Used to mark a function as an error callback
*/
errorCallback: 'errorCallback',
/**
* Used to import external convo script code
*/
import: 'import',
/**
* Matches an import by path. The match value can use wild cards are be a regular expression.
* Regular expressions start with a (!) followed by a space then the regular expression pattern
*
* @example // By path
* (@)importMatch ./company-policies.md
*
* @example // wildcard
* (@)importMatch *policies.md
*
* @example // regular expression
* (@)importMatch ! policies\.(md|mdx)$
*/
importMatch: 'importMatch',
importRole: 'importRole',
/**
* Causes a message to be concatenated with the previous message. Both the message the tag
* is attached to and the previous message must be content messages or the tag is ignored.
* When a message is concatenated to another message all other tags except the condition
* tag are ignored.
*/
concat: 'concat',
/**
* Instructs the LLM to call the specified function. The values "none", "required", "auto" have
* a special meaning. If no name is given the special "required" value is used.
* - none: tells the LLM to not call any functions
* - required: tells the LLM it must call a function, any function.
* - auto: tells the LLM it can call a function respond with a text response. This is the default behaviour.
*/
call: 'call',
/**
* Causes the message to be evaluated as code. The code should be contained in a markdown code block.
*/
eval: 'eval',
/**
* Id of the user that created the message
*/
userId: 'userId',
/**
* Causes all white space in a content message to be preserved. By define all content message
* whitespace is preserved.
*/
preSpace: 'preSpace',
/**
* Indicates a message is the system message used to give an LLM instructions on how to use
* agents
*/
agentSystem: 'agentSystem',
/**
* Defines capabilities for a message
*/
cap: 'cap',
/**
* Conversation ID
*/
cid: 'cid',
/**
* Adds a message to a transform group. Transform groups are used to transform assistant output.
* The transform tags value can be the name of a type or empty. Transform groups are ran after all
* text responses from the assistant. Transform messages are not added to the flattened conversation.
*/
transform: 'transform',
/**
* Sets the name of the transform group a message will be added to when the transform tag is used.
*/
transformGroup: 'transformGroup',
/**
* If present on a transform message the source message processed will be hidden from the user
* but still visible to the LLM
*/
transformHideSource: 'transformHideSource',
/**
* Overrides `transformHideSource` and `transformRemoveSource`
*/
transformKeepSource: 'transformKeepSource',
/**
* If present on a transform message the source message processed will not be added to the
* conversation
*/
transformRemoveSource: 'transformRemoveSource',
/**
* If present the transformed message has the `renderOnly` tag applied to it causing it to be
* visible to the user but not the LLM.
*/
transformRenderOnly: 'transformRenderOnly',
/**
* A transform condition that will control if the component tag can be passed to the created message
*/
transformComponentCondition: 'transformComponentCondition',
/**
* Messages created by the transform will include the defined tag
* @example (@)transformTag renderTarget sideBar
*/
transformTag: 'transformTag',
/**
* A shortcut tag combines the `transform`, `transformTag`, `transformRenderOnly`, `transformComponentCondition`
* and `transformHideSource` tags to create a transform that renders a
* component based on the data structure of a named
* struct.
* @usage (@)transformComponent [groupName] {componentName} {propType} [?[!] condition]
*
* Renders the CarView component after every assistant message. The transform is using the default transform group.
* @example (@)transformComponent CarView CarProps
*
* Renders the CatPickerView component if the transformed message is a json object with the "type" key is set to cat.
* The transform is in the CatPicker transform group.
* @example (@)transformComponent CatPicker CatPickerView AnimalPrefs ? type cat
*
* Renders the AnimalsOtherThanPickerView component if the transformed message is a json object with the "type" key is NOT set to cat.
* The transform is in the default transform group.
* @example (@)transformComponent AnimalsOtherThanPickerView AnimalPrefs ?! type cat
*/
transformComponent: 'transformComponent',
/**
* Applied to messages created by a transform
*/
createdByTransform: 'createdByTransform',
/**
* When applied to a message the message will be included in all transform prompts. It is common
* to apply includeInTransforms to system messages
*/
includeInTransforms: 'includeInTransforms',
/**
* Describes what the result of the transform is
*/
transformDescription: 'transformDescription',
/**
* If applied to a transform message it will not be passed through a filter prompt
*/
transformRequired: 'transformRequired',
/**
* When applied to a message the transform filter will be used to select which transforms to
* to select. The default filter will list all transform groups and their descriptions to select
* the best fitting transform for the assistants response
*/
transformFilter: 'transformFilter',
/**
* If applied to a transform message the transform must be explicity enabled applying the `enableTransform`
* tag to another message or calling the enableTransform function.
*/
transformOptional: 'transformOptional',
/**
* Applied to transform output messages when overwritten by a transform with a higher priority
*/
overwrittenByTransform: 'overwrittenByTransform',
/**
* Explicitly enables a transform. Transforms are enabled by default unless the transform has
* the `transformOptional` tag applied.
*/
enableTransform: 'enableTransform',
/**
* Defines a component to render a function result
*/
renderer: 'renderer',
/**
* Indicates a message is a standard system message. Standard system messages are used to
* implement common patterns such as the moderator pattern.
*/
stdSystem: 'stdSystem',
/**
* Prevents a message from accepting modifiers and allows modifiers to flow through the message
*/
disableModifiers: 'disableModifiers',
/**
* Attached to a message to indicate the user has reached their limit of tokens
*/
tokenLimit: 'tokenLimit',
router: 'router',
routeTo: 'routeTo',
routeFrom: 'routeFrom',
/**
* Use to mark a function as a message handler. Functions tagged with `@messageHandler` will
* be made local and not visible to LLMs.
*/
messageHandler: 'messageHandler',
/**
* Name of the prop that the head value of handles messages are passed to.
* @default "name"
*/
messageHandlerHeadProp: 'messageHandlerHeadProp',
/**
* When applied to a message handler the handler will assume the location of the message
* the handler is handling
*/
assumeHandledMessageLocation: 'assumeHandledMessageLocation',
};
/**
* Functions marked with tags defined in `localFunctionTags` will be marked as local and not visible
* to LLMs.
*/
export const localFunctionTags = [
convoTags.on,
convoTags.messageHandler,
];
/**
* Tags that are allowed to have dynamic expressions as the value when using the equals operator.
* @example (@)condition = eq(name "Bob")
*/
export const convoDynamicTags = [
convoTags.condition,
convoTags.disabled,
convoTags.taskName,
convoTags.taskDescription,
convoTags.json,
convoTags.routeTo,
convoTags.routeFrom,
];
/**
* Tags whom have a dynamic expression will be evaluated as an anonymous type
*/
export const convoAnonTypeTags = [
convoTags.json,
];
/**
* Prefix used to define anonymous types
*/
export const convoAnonTypePrefix = 'AnonType_';
/**
* JSDoc tags can be used in combination with the Convo-Lang CLI to import types, components and
* functions from TypeScript.
*/
export const convoJsDocTags = {
/**
* Marks a function or class as a convo component
*/
convoComponent: 'convoComponent',
/**
* When used with a component the source message that gets transform into the component should be
* kept visible in the conversation
*/
convoKeepSource: 'convoKeepSource',
/**
* Used to ignore properties in a type
*/
convoIgnore: 'convoIgnore',
/**
* Marks a interface or type as a type to define in convo
*/
convoType: 'convoType',
/**
* Marks a function as a function to define in convo
*/
convoFn: 'convoFn',
/**
* Used with the convoFn tag to mark a function as local. When a function is local it is not
* exposed to the LLM but can be called from convo scripts.
*/
convoLocal: 'convoLocal'
};
export const convoTaskTriggers = {
/**
* Triggers a text message is received. Function calls will to trigger.
*/
onResponse: 'onResponse'
};
export const commonConvoCacheTypes = {
localStorage: 'localStorage',
memory: 'memory',
vfs: 'vfs',
userVfs: 'userVfs',
};
/**
* In the browser the default cache type is local storage and on the backend vfs is the default cache type.
*/
export const defaultConvoCacheType = globalThis.window ? commonConvoCacheTypes.localStorage : commonConvoCacheTypes.vfs;
export const convoDateFormat = "yyyy-MM-dd'T'HH:mm:ssxxx";
export const defaultConvoRenderTarget = 'default';
export const defaultConvoTransformGroup = 'default';
export const convoStdImportPrefix = 'std://';
export const getConvoDateString = (date = new Date()) => {
return format(date, convoDateFormat);
};
export const defaultConvoVisionSystemMessage = ('If the user references a markdown image without a ' +
'description or the description can not answer the user\'s question or ' +
`complete the user\`s request call the ${convoFunctions.queryImage} function. ` +
'Do not use the URL of the image to make any assumptions about the image.');
export const defaultConvoVisionResponse = 'Unable to answer or respond to questions or requests for the given image or images';
export const allowedConvoDefinitionFunctions = [
convoNewFnName,
convoStructFnName,
convoMapFnName,
convoFunctions.mapWithCapture,
convoArrayFnName,
convoEnumFnName,
convoJsonMapFnName,
convoJsonArrayFnName,
convoFunctions.getState,
convoFunctions.enableRag,
convoFunctions.clearRag,
convoFunctions.defineForm,
convoFunctions.uuid,
convoFunctions.shortUuid,
convoFunctions.getVar,
convoFunctions.setVar,
convoFunctions.idx,
convoFunctions.setDefault,
convoFunctions.enableTransform,
convoFunctions.enableAllTransforms,
convoFunctions.isUndefined,
convoFunctions.secondMs,
convoFunctions.minuteMs,
convoFunctions.hourMs,
convoFunctions.dayMs,
convoFunctions.aryFindMatch,
convoFunctions.aryRemoveMatch,
'print',
'setObjDefaults',
'is',
'and',
'or',
'not',
'eq',
'gt',
'gte',
'lt',
'lte',
'isIn',
'contains',
'regexMatch',
'starMatch',
'deepCompare',
'add',
'sub',
'mul',
'div',
'mod',
'pow',
'inc',
'dec',
'rand',
'now',
'dateTime',
'encodeURI',
'encodeURIComponent',
];
export const passthroughConvoInputType = 'FlatConvoConversation';
export const passthroughConvoOutputType = 'ConvoCompletionMessageAry';
export const createOptionalConvoValue = (value) => {
return {
[convoObjFlag]: 'optional',
value,
};
};
export const createConvoType = (typeDef) => {
typeDef[convoObjFlag] = 'type';
return typeDef;
};
export const createConvoBaseTypeDef = (type) => {
return {
[convoObjFlag]: 'type',
type,
};
};
export const makeAnyConvoType = (type, value) => {
if (!value) {
return value;
}
value[convoObjFlag] = 'type';
value['type'] = type;
return value;
};
export const createConvoScopeFunction = (fnOrCtrl, fn) => {
if (typeof fnOrCtrl === 'function') {
fnOrCtrl[convoScopeFunctionMarker] = true;
return fnOrCtrl;
}
if (!fn) {
fn = (scope) => scope.paramValues ? scope.paramValues[scope.paramValues.length - 1] : undefined;
}
if (fnOrCtrl) {
fn[convoFlowControllerKey] = fnOrCtrl;
}
fn[convoScopeFunctionMarker] = true;
return fn;
};
export const isConvoScopeFunction = (value) => {
return (value && value[convoScopeFunctionMarker]) ? true : false;
};
export const setConvoScopeError = (scope, error) => {
if (typeof error === 'string') {
error = {
message: error,
statement: scope?.s,
};
}
if (!scope) {
throw error;
}
scope.error = error;
if (scope.onError) {
const oe = scope.onError;
delete scope.onError;
delete scope.onComplete;
for (let i = 0; i < oe.length; i++) {
oe[i]?.(error);
}
}
};
const notWord = /\W/g;
const newline = /[\n\r]/g;
const multiTagReg = /^(\w+)__\d+$/;
export const convoTagMapToCode = (tagsMap, append = '', tab = '') => {
const out = [];
for (const e in tagsMap) {
const v = tagsMap[e];
const nameMatch = multiTagReg.exec(e);
out.push(`${tab}@${(nameMatch?.[1] ?? e).replace(notWord, '_')}${v ? ' ' + v.replace(newline, ' ') : ''}`);
}
return out.join('\n') + append;
};
export const containsConvoTag = (tags, tagName) => {
if (!tags) {
return false;
}
for (let i = 0; i < tags.length; i++) {
if (tags[i]?.name === tagName) {
return true;
}
}
return false;
};
export const getConvoTag = (tags, tagName) => {
if (!tags) {
return undefined;
}
for (let i = 0; i < tags.length; i++) {
const tag = tags[i];
if (tag?.name === tagName) {
return tag;
}
}
return undefined;
};
export const getConvoFnMessageByTag = (tag, messages, startIndex = 0) => {
if (!messages) {
return undefined;
}
for (let i = startIndex; i < messages.length; i++) {
const msg = messages[i];
if (!msg || !msg.tags || !msg.fn || msg.fn.call) {
continue;
}
for (const t of msg.tags) {
if (t.name === tag) {
return msg;
}
}
}
return undefined;
};
export const findConvoMessage = (messages, { tag, tagValue, role, startIndex = 0, }) => {
if (!messages) {
return undefined;
}
for (let i = startIndex; i < messages.length; i++) {
const msg = messages[i];
if (!msg || !msg.tags || (role !== undefined && msg.role !== role)) {
continue;
}
if (tag !== undefined) {
for (const t of msg.tags) {
if (t.name === tag &&
(tagValue === undefined ? true : t.value === tagValue)) {
return msg;
}
}
continue;
}
return msg;
}
return undefined;
};
export const getConvoFnByTag = (tag, messages, startIndex = 0) => {
return getConvoFnMessageByTag(tag, messages, startIndex)?.fn;
};
export const convoTagsToMap = (tags, exe) => {
const map = {};
for (const t of tags) {
let name = t.name;
if (name in map) {
let i = 2;
while (`${t.name}__${i}` in map) {
i++;
}
name = `${t.name}__${i}`;
}
if (t.statement) {
const values = exe.getTagStatementValue(t);
let value;
if (values.length === 1) {
let value = values[0];
if (value && typeof value === 'object') {
value = JSON.stringify(value);
}
}
else {
value = JSON.stringify(value);
}
if (value === false || value === null || value === undefined) {
map[name] = undefined;
}
else {
map[name] = value + '';
}
}
else {
map[name] = t.value;
}
}
return map;
};
export const mapToConvoTags = (map) => {
const tags = [];
for (const e in map) {
tags.push({
name: e,
value: map[e]
});
}
return tags;
};
export const getFlatConvoTagValues = (name, tags) => {
const values = [];
if (!tags || !(name in tags)) {
return values;
}
values.push(tags[name] ?? '');
let i = 2;
while (`${name}__${i}` in tags) {
i++;
values.push(tags[`${name}__${i}`] ?? '');
}
return values;
};
const transformTagReg = /^\s*(\w+)(.*)/;
export const parseConvoTransformTag = (value) => {
const match = transformTagReg.exec(value);
if (!match) {
return undefined;
}
return {
name: match[1] ?? '',
value: match[2]?.trim(),
};
};
export const createConvoMetadataForStatement = (statement) => {
return {
name: ((statement.set && !statement.setPath) ?
statement.set
: statement.label ?
statement.label
:
undefined),
comment: statement.comment,
tags: statement.tags,
};
};
export const getConvoMetadata = (value) => {
return value?.[convoMetadataKey];
};
export const getConvoStructPropertyCount = (value) => {
const metadata = getConvoMetadata(value);
return metadata?.properties ? getObjKeyCount(metadata.properties) : 0;
};
export const convoLabeledScopeFnParamsToObj = (scope, fnParams) => {
return convoParamsToObj(scope, undefined, false, fnParams);
};
export const convoLabeledScopeParamsToObj = (scope) => {
return convoParamsToObj(scope, undefined, false);
};
export const convoParamsToObj = (scope, unlabeledMap, unlabeledKey = true, fallbackFnParams) => {
const obj = {};