@iyio/convo-lang
Version:
A conversational language.
687 lines (686 loc) • 22 kB
TypeScript
import { CodeParsingResult, JsonScheme, MarkdownLine } from '@iyio/common';
import type { ZodObject, ZodType } from 'zod';
import type { Conversation } from "./Conversation";
import type { ConvoExecutionContext } from './ConvoExecutionContext';
export type ConvoMessageType = 'text' | 'function';
export declare const convoValueConstants: readonly ["true", "false", "null", "undefined"];
export type ConvoValueConstant = (typeof convoValueConstants)[number];
export declare const convoNonFuncKeywords: readonly ["in"];
export type ConvoNonFuncKeyword = (typeof convoNonFuncKeywords)[number];
export declare const convoReservedRoles: readonly ["call", "do", "result", "define", "debug", "end"];
export type ConvoReservedRole = (typeof convoReservedRoles)[number];
export declare const convoObjFlag = "**convo**";
export interface ConvoTag {
name: string;
value?: string;
}
export type ConvoErrorType = ('invalid-return-value-type' | 'function-call-parse-count' | 'function-call-args-suspended' | 'unexpected-base-type' | 'invalid-args' | 'proxy-call-not-supported' | 'function-not-defined' | 'function-return-type-not-defined' | 'function-args-type-not-defined' | 'function-args-type-not-an-object' | 'suspended-scheme-statements-not-supported' | 'zod-object-expected' | 'scope-waiting' | 'suspension-parent-not-found' | 'variable-ref-required' | 'max-type-conversion-depth-reached' | 'unknown-json-scheme-type' | 'invalid-scheme-type' | 'invalid-variable-name' | 'invalid-function-name' | 'invalid-type-name' | 'invalid-register-only-function' | 'invalid-role' | 'use-of-reserved-role-not-allowed' | 'invalid-message-response-scheme' | 'missing-defaults');
export interface ConvoErrorReferences {
message?: ConvoMessage;
fn?: ConvoFunction;
statement?: ConvoStatement;
statements?: ConvoStatement[];
completion?: ConvoCompletionMessage;
baseType?: ConvoBaseType;
}
export declare const allConvoCapabilityAry: readonly ["vision"];
export type ConvoCapability = typeof allConvoCapabilityAry[number];
export declare const isConvoCapability: (value: any) => value is ConvoCapability;
/**
* Can be a text message or function definition
*/
export interface ConvoMessage {
role?: string;
content?: string;
description?: string;
statement?: ConvoStatement;
fn?: ConvoFunction;
tags?: ConvoTag[];
markdown?: MarkdownLine[];
/**
* The target render area of the message.
*/
renderTarget?: string;
/**
* A variable to assign the content or jsonValue of the message to
*/
assignTo?: string;
/**
* The value of the message parsed as json
*/
jsonValue?: any;
/**
* If the message has the `@component` tag the message will be marked as a component. If component
* equals true then the component is an unnamed component. Components are by default cause the
* message to be renderOnly
*/
component?: string | boolean;
/**
* If true the message should be rendered but not sent to LLMs
*/
renderOnly?: boolean;
sourceUrl?: string;
sourceId?: string;
sourceName?: string;
/**
* If true the message should be clickable and when clicked the content of the message should be
* added to the conversation.
*/
isSuggestion?: boolean;
/**
* Thread Id. Conversations can be divided into threads and during evaluation threads can be
* used to control which messages are included.
*/
tid?: string;
}
export interface ConvoMessagePart {
id?: string;
content?: string;
hidden?: boolean;
}
export interface ConvoStatement {
/**
* Raw value
*/
value?: any;
/**
* name of function to invoke
*/
fn?: string;
/**
* Dot path used with fn
*/
fnPath?: string[];
/**
* Args to pass to fn if defined
*/
params?: ConvoStatement[];
/**
* Name of a variable to set the function call result or value to.
*/
set?: string;
/**
* Dot path used with set
*/
setPath?: string[];
/**
* Name of a referenced variable
*/
ref?: string;
/**
* Dot path used with ref
*/
refPath?: string[];
/**
* Label optional - If true the statement is option. Used in com
*/
opt?: boolean;
/**
* Statement label
*/
label?: string;
/**
* Name of a non function keyword
*/
keyword?: string;
comment?: string;
tags?: ConvoTag[];
/**
* If true the statement has the shared tag and assignment of variables will be stored in the
* shared variable scope.
*/
shared?: boolean;
/**
* Source index start
*/
s: number;
/**
* Source index end. The index is non-inclusive.
*/
e: number;
/**
* Source index close. Only used with function calls. The index is non-inclusive.
*/
c?: number;
/**
* If true the statement is a match case. Match cases are used with switch statements. Match
* case functions include (case, test and default)
*/
mc?: boolean;
/**
* If the statement has child match case statements.
*/
hmc?: boolean;
/**
* Used to start the source code of a statement. Source is most commonly used by message templates.
*/
source?: string;
/**
* If true the statement in font of the current pipe statement will be piped to the statement
* behind the current pipe statement. Pipe statements are converted to calls to the pipe function
* when a convo script is parsed and will to be present in a fully parsed syntax tree
*/
_pipe?: boolean;
/**
* If true the statement has pipe statements in it's args.
*/
_hasPipes?: boolean;
}
export interface ConvoMessageAndOptStatement {
message: ConvoMessage;
statement?: ConvoStatement;
}
export interface ConvoFunction {
name: string;
modifiers: string[];
/**
* Name of a type variable that the function returns. If undefined the function
* can return any type.
*/
returnType?: string;
/**
* If true the function is a local statement that should only be called by other functions in the
* current convo script
*/
local: boolean;
/**
* If true it has been requested that the function be called.
*/
call: boolean;
/**
* If true the function is a collection of top level statements
*/
topLevel: boolean;
/**
* If true the function only defines types
*/
definitionBlock?: boolean;
description?: string;
/**
* The body statements of the function. If a function does not have a body then it is either
* a function interface or a call or called statement.
*/
body?: ConvoStatement[];
params: ConvoStatement[];
paramType?: string;
}
/**
* Filters what messages are included in a conversation based on the rules of the filter.
* Exclusive rules take priority over inclusive rules.
*/
export interface ConvoThreadFilter {
/**
* Ids of threads to include. If defined only messages with a thread id that is included in
* includeThreads will be included in the evaluation of a conversation. Messages without a
* threadId will be included if includeNonThreaded is true.
*/
includeThreads?: string[];
/**
* If true and a message does not have a thread id the message is included.
*/
includeNonThreaded?: boolean;
/**
* Ids of threads to excluded. If defined any messages with a thread id that is included in
* excludeThreads will be excluded from the evaluation of a conversation. Messages without a
* threadId will be excluded if excludeNonThreaded is true.
*/
excludeThreads?: string[];
/**
* If true and a message does not have a thread id the message is excluded.
*/
excludeNonThreaded?: boolean;
}
export type ConvoParsingResult = CodeParsingResult<ConvoMessage[]>;
export interface ConvoScopeError {
message: string;
error?: any;
statement?: ConvoStatement;
}
export interface OptionalConvoValue<T = any> {
[convoObjFlag]: 'optional';
value?: T;
}
export declare const isOptionalConvoValue: (value: any) => value is OptionalConvoValue;
export interface ConvoFlowControllerDataRef {
ctrlData?: any;
childCtrlData?: Record<string, ConvoFlowControllerDataRef>;
}
export declare const convoFlowControllerKey: unique symbol;
export interface ConvoFlowController {
/**
* If true only the last param value is stored in the scopes param value array
*/
discardParams?: boolean;
usesLabels?: boolean;
/**
* If true return control flow should be caught
*/
catchReturn?: boolean;
/**
* If true break control flow should be caught
*/
catchBreak?: boolean;
sourceFn?: ConvoFunction;
/**
* If true the ctrl data will be stored by the parent scope of the ctrl
* This is useful for ctrls that manage loops or iterators.
*/
keepData?: boolean;
startParam?(scope: ConvoScope, parentScope: ConvoScope | undefined, ctx: ConvoExecutionContext): number | false;
nextParam?(scope: ConvoScope, parentScope: ConvoScope | undefined, paramStatement: ConvoStatement, ctx: ConvoExecutionContext): number | false;
shouldExecute?(scope: ConvoScope, parentScope: ConvoScope | undefined, ctx: ConvoExecutionContext): boolean;
transformResult?(value: any, scope: ConvoScope, parentScope: ConvoScope | undefined, ctx: ConvoExecutionContext): any;
}
export type ConvoScopeFunction = (scope: ConvoScope, ctx: ConvoExecutionContext) => any;
export declare const convoScopeFnKey: unique symbol;
export declare const convoScopeParentKey: unique symbol;
export interface ConvoScope {
/**
* suspension id
*/
si?: string;
onComplete?: ((value: any) => void)[];
onError?: ((error: any) => void)[];
/**
* value
*/
v?: any;
/**
* If true the control flow should return from the current function
*/
r?: boolean;
/**
* If true the control flow should break the current loop
*/
bl?: boolean;
/**
* If defined li tells control flow that the statement at the given index is the body of a
* loop statement
*/
li?: number;
/**
* index
*/
i: number;
s: ConvoStatement;
/**
* Wait id. Id of another scope to wait for before resuming
*/
wi?: string;
/**
* Parent suspension id
*/
pi?: string;
/**
* Suspension Finished
*/
fi?: boolean;
[convoScopeFnKey]?: ConvoScopeFunction;
/**
* Param values used with function statements
*/
paramValues?: any[];
/**
* Maps label names to param indexes
*/
labels?: Record<string, number | OptionalConvoValue<number>>;
error?: ConvoScopeError;
/**
* If true the scope is defining a type
*/
td?: boolean;
ctrlData?: any;
/**
* Similar to ctrlData but can be used by child statement controls to store data between
* statements executions. Used by the (in) function to store iterator position.
*/
childCtrlData?: Record<string, any>;
fromIndex?: number;
gotoIndex?: number;
/**
* When this index is reached flow control should break. The statement at the index will not be executed.
* breakIndex (bi) does not effect the scopes parent.
*/
bi?: number;
it?: ConvoIterator;
/**
* Variable scope for the scope. When a function is executed all scopes generated will share the
* same vars scope object. This is important to remember when serializing scope objects.
*/
vars: Record<string, any>;
/**
* If true metadata should be captured. Metadata capturing is implemented in ConvoFlowControllers.
*/
cm?: boolean;
/**
* Stores the current switch value
*/
sv?: any;
/**
* If true the scope is used as the default scope object. This property is used for internal
* optimization and should be ignored
*/
_d?: boolean;
}
export interface ConvoIterator {
/**
* Index of current iteration item
*/
i: number;
/**
* Keys of item currently being iterated. Used when iterating over objects
*/
keys?: string[];
}
export interface ConvoKeyValuePair {
key: string;
value: any;
}
export declare const convoBaseTypes: readonly ["string", "number", "int", "boolean", "time", "void", "any", "map", "array"];
export type ConvoBaseType = (typeof convoBaseTypes)[number];
export declare const isConvoBaseType: (value: any) => value is ConvoBaseType;
export interface ConvoType {
[convoObjFlag]: 'type';
type: string;
enumValues?: any[];
}
export declare const isConvoType: (value: any) => value is ConvoType;
export interface ConvoMarkdownLine {
[convoObjFlag]: 'md';
line: MarkdownLine;
}
export declare const isConvoMarkdownLine: (value: any) => value is ConvoMarkdownLine;
export interface FlatConvoMessage {
role: string;
isUser?: boolean;
content?: string;
/**
* A function that can be called
*/
fn?: ConvoFunction;
/**
* Params type of the function that can be called
*/
fnParams?: ZodObject<any>;
/**
* The target render area of the message.
*/
renderTarget?: string;
/**
* A function that was called
*/
called?: ConvoFunction;
/**
* The parameters that where passed to the function
*/
calledParams?: any;
/**
* The value the called function returned
*/
calledReturn?: any;
/**
* If true message was generated at the edge of the conversation, meaning the template expressions
* where evaluated with the latest variables.
*/
edge?: boolean;
/**
* Used with messages that set variables such as define and result messages.
*/
setVars?: Record<string, any>;
tags?: Record<string, string | undefined>;
/**
* The model the message has been requested to be completed with.
*/
responseModel?: string;
responseEndpoint?: string;
responseFormat?: string;
responseFormatTypeName?: string;
responseFormatIsArray?: boolean;
responseAssignTo?: string;
task?: string;
/**
* If the message has the `@component` tag the message will be marked as a component. If component
* equals true then the component is an unnamed component.
*/
component?: string | boolean;
/**
* If true the message should be rendered but not sent to LLMs
*/
renderOnly?: boolean;
markdown?: MarkdownLine[];
sourceUrl?: string;
sourceId?: string;
sourceName?: string;
/**
* If true the message should be clickable and when clicked the content of the message should be
* added to the conversation.
*/
isSuggestion?: boolean;
/**
* Thread Id. Conversations can be divided into threads and during evaluation threads can be
* used to control which messages are included.
*/
tid?: string;
}
export interface ConvoCompletionMessage extends Partial<ConvoTokenUsage> {
role?: string;
content?: string;
callFn?: string;
callParams?: any;
tags?: Record<string, string | undefined>;
model?: string;
format?: string;
formatTypeName?: string;
formatIsArray?: boolean;
assignTo?: string;
endpoint?: string;
}
export interface ConvoCompletionService {
completeConvoAsync(flat: FlatConvoConversation): Promise<ConvoCompletionMessage[]>;
}
export type ConvoCompletionStatus = 'complete' | 'busy' | 'error' | 'disposed';
export type ConvoRagMode = boolean | number;
export declare const isConvoRagMode: (value: any) => value is ConvoRagMode;
export interface ConvoFnCallInfo {
name: string;
message: ConvoMessage;
fn: ConvoFunction;
returnValue: any;
}
export interface ConvoCompletion {
status: ConvoCompletionStatus;
message?: ConvoCompletionMessage;
messages: ConvoCompletionMessage[];
error?: any;
exe?: ConvoExecutionContext;
lastFnCall?: ConvoFnCallInfo;
returnValues?: any[];
task: string;
}
export interface FlatConvoConversation {
exe: ConvoExecutionContext;
vars: Record<string, any>;
messages: FlatConvoMessage[];
conversation: Conversation;
capabilities: ConvoCapability[];
task: string;
/**
* Maps task triggers to tasks.
* triggerName -> task array
*/
taskTriggers?: Record<string, string[]>;
templates?: ConvoMessageTemplate[];
markdownVars: Record<string, ConvoMarkdownLine | string>;
/**
* If defined the debug function should be written to with debug info.
*/
debug?: (...args: any[]) => void;
ragMode?: ConvoRagMode;
ragPrefix?: string;
ragSuffix?: string;
}
export interface ConvoExecuteResult {
scope: ConvoScope;
value?: any;
valuePromise?: Promise<any>;
}
/**
* A function that prints the args it is passed and returns the last arg.
*/
export type ConvoPrintFunction = (...args: any[]) => any;
export interface ConvoMetadata {
name?: string;
comment?: string;
tags?: ConvoTag[];
properties?: Record<string, ConvoMetadata>;
}
export interface ConvoPipeTarget {
convoPipeSink(value: any): Promise<any> | any;
}
export declare const isConvoPipeTarget: (value: any) => value is ConvoPipeTarget;
export interface ConvoGlobal extends ConvoPipeTarget {
conversation?: Conversation;
exe?: ConvoExecutionContext;
}
export interface ConvoTypeDef<T = any> {
name: string;
type: ZodType<T> | JsonScheme;
}
export interface ConvoVarDef<T = any> {
name: string;
value: T;
}
export interface ConvoFunctionDef<P = any, R = any> {
description?: string;
name: string;
local?: boolean;
paramsType?: ZodType<P>;
paramsJsonScheme?: JsonScheme;
returnTypeName?: string;
returnScheme?: ConvoTypeDef<P>;
/**
* Convo function body code
*/
body?: string;
callback?: (params: P) => R;
scopeCallback?: ConvoScopeFunction;
disableAutoComplete?: boolean;
/**
* If true the function will only be registered and will not be added the to conversation code.
* This is useful for defining library functions or functions that don't need to be tracked as
* part of a conversation. Setting registerOnly will for the function to be local.
*/
registerOnly?: boolean;
}
export interface ConvoDefItem<T = any, R = any> {
type?: ConvoTypeDef<T>;
var?: ConvoVarDef<T>;
fn?: ConvoFunctionDef<T, R>;
/**
* If true the item will be registered but its code will not be added to the conversation.
*/
hidden?: boolean;
types?: Record<string, ConvoTypeDef['type']>;
vars?: Record<string, ConvoVarDef['value']>;
fns?: Record<string, Omit<ConvoFunctionDef, 'name'> | ((params?: any) => any)>;
}
export interface ConvoAppend {
text: string;
messages: ConvoMessage[];
}
/**
* Completes a flattened conversation. Flatten conversations are conversation where all template
* variables have been applied and the conversation is ready to be passed to an LLM or other
* service that will use the flatten view of the conversation.
*/
export type ConvoFlatCompletionCallback = (flat: FlatConvoConversation) => Promise<ConvoCompletionMessage[]> | ConvoCompletionMessage[];
export interface ConvoTokenUsage {
inputTokens: number;
outputTokens: number;
tokenPrice: number;
}
export interface ConvoMessagePrefixOptions {
includeTokenUsage?: boolean;
msg?: ConvoCompletionMessage;
}
export interface FlattenConvoOptions {
/**
* If true the flatten view of the conversation will be set as the current flatten version
* @default task === "default"
*/
setCurrent?: boolean;
/**
* The name of the current task being executed.
* @default "default"
*/
task?: string;
discardTemplates?: boolean;
threadFilter?: ConvoThreadFilter;
}
export interface ConvoSubTask {
name: string;
promise: Promise<ConvoCompletion>;
}
export interface ConvoCompletionOptions {
task?: string;
append?: string;
/**
* If true completion should stop and return just before a function is to be called
*/
returnOnCall?: boolean;
/**
* If true completion should stop and return after a function is called before sending a response
* to the LLM
*/
returnOnCalled?: boolean;
threadFilter?: ConvoThreadFilter;
/**
* If defined the token usage will be added to the defined usage.
*/
usage?: ConvoTokenUsage;
debug?: boolean;
}
export interface ConvoMessageTemplate {
message: ConvoMessage;
name?: string;
watchPath?: string;
matchValue?: string;
startValue?: any;
}
export interface CloneConversationOptions {
systemOnly?: boolean;
noFunctions?: boolean;
}
export interface ConvoDocumentReference {
content: string;
sourceId?: string;
sourceName?: string;
sourceUrl?: string;
}
export interface ConvoRagContext {
params: Record<string, any>;
tolerance: number;
lastMessage: FlatConvoMessage;
flat: FlatConvoConversation;
conversation: Conversation;
}
export type ConvoRagCallback = (ragContext: ConvoRagContext) => ConvoDocumentReference | null | Promise<ConvoDocumentReference | null>;
export interface AppendConvoMessageObjOptions {
disableAutoFlatten?: boolean;
appendCode?: boolean;
}
export interface ConvoImport {
name: string;
}
export interface ConvoImportResult {
name: string;
/**
* Convo to be inserted before the import
*/
convo?: string;
/**
* A type or set of types that will be converted to a convo type and imported
*/
type?: ConvoTypeDef | ConvoTypeDef[];
}
export type ConvoImportHandler = (_import: ConvoImport) => ConvoImportResult | null | undefined | Promise<ConvoImportResult | null | undefined>;