@convo-lang/convo-lang
Version:
The language of AI
260 lines • 8.87 kB
JavaScript
import { getErrorMessage } from "@iyio/common";
import { convoMessageToStringSafe, convoRoles, convoTags, convoVars, defaultConvoNodeId } from "./convo-lib.js";
export const applyConvoNodeOrdering = (messages, conversation, appendTail = messages[messages.length - 1]?.role === convoRoles.goto) => {
const inlineMessages = {};
const functionMessages = {};
const systemMessages = {};
const scriptMessages = {};
const allMessages = {};
let currentNodeId;
for (let i = 0; i < messages.length; i++) {
const msg = messages[i];
if (!msg) {
continue;
}
switch (msg.role) {
case convoRoles.node: {
currentNodeId = msg.nodeId;
if (currentNodeId === defaultConvoNodeId) {
currentNodeId = undefined;
}
break;
}
case convoRoles.to:
case convoRoles.from:
case convoRoles.exit:
// do nothing. Messages should not be added
break;
case convoRoles.goto:
case convoRoles.nodeEnd:
case convoRoles.exitGraph:
currentNodeId = undefined;
break;
default:
if (currentNodeId) {
if (conversation.isSystemMessage(msg)) {
(systemMessages[currentNodeId] ?? (systemMessages[currentNodeId] = [])).push(msg);
}
else if (msg.fn && !msg.fn.topLevel && !msg.fn.call) {
(functionMessages[currentNodeId] ?? (functionMessages[currentNodeId] = [])).push(msg);
}
else if (conversation.isUserMessage(msg) || conversation.isAssistantMessage(msg)) {
messages.splice(i, 1);
i--;
(inlineMessages[currentNodeId] ?? (inlineMessages[currentNodeId] = [])).push(msg);
}
else if (msg.role === convoRoles.define || msg.role === convoRoles.do) {
messages.splice(i, 1);
i--;
(scriptMessages[currentNodeId] ?? (scriptMessages[currentNodeId] = [])).push(msg);
(inlineMessages[currentNodeId] ?? (inlineMessages[currentNodeId] = [])).push(msg, {
role: convoRoles._sourceRef,
content: convoMessageToStringSafe(msg)
});
}
(allMessages[currentNodeId] ?? (allMessages[currentNodeId] = [])).push(msg);
}
break;
}
}
if (appendTail) {
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i];
if (msg?.role !== convoRoles.goto) {
continue;
}
const insert = inlineMessages[msg.gotoNodeId ?? ''];
if (!insert) {
break;
}
messages.splice(i + 1, 0, ...insert.map(m => ({ ...m })));
i += insert.length;
break;
}
}
return { inlineMessages, functionMessages, systemMessages, scriptMessages, allMessages };
};
export const getConvoNodeOutput = (messages, exe, startIndex) => {
for (let i = startIndex ?? (messages.length - 1); i >= 0; i--) {
const msg = messages[i];
if (!msg) {
continue;
}
if ((msg.isUser || msg.isAssistant) && msg.content) {
return msg.content;
}
if (msg.role === convoRoles.result) {
return exe.getVar(convoVars.__return);
}
}
return undefined;
};
export const convoTagToNodeRoute = (tag) => {
const route = {
toNodeId: tag.label || defaultConvoNodeId,
nlCondition: tag.statement ? undefined : tag.value,
condition: tag.statement,
};
if (route.nlCondition === 'else') {
delete route.nlCondition;
}
if (tag.name === convoTags.exit) {
route.exit = true;
}
if (route.toNodeId === 'auto') {
if (route.nlCondition) {
route.auto = route.nlCondition.split(/\s+/).filter(n => n);
delete route.nlCondition;
}
else {
route.auto = true;
}
}
return route;
};
export const getConvoNodeMessages = (nodeId, messages, includeRoutes = true) => {
let inNode = false;
const nodeMessages = [];
for (const msg of messages) {
switch (msg.role) {
case convoRoles.node:
if (msg.nodeId === nodeId) {
inNode = true;
nodeMessages.push(msg);
}
else {
inNode = false;
}
break;
case convoRoles.nodeEnd:
case convoRoles.goto:
case convoRoles.exitGraph:
inNode = false;
break;
case convoRoles.to:
case convoRoles.from:
case convoRoles.exit:
if (includeRoutes && inNode) {
nodeMessages.push(msg);
}
break;
default:
if (inNode) {
nodeMessages.push(msg);
}
break;
}
}
return nodeMessages;
};
export const getExtendedConvoNodeDescription = (node, source, sourceMessages) => {
const messages = source.messages.filter(m => m.nodeId === node.nodeId);
const srcMsgs = getConvoNodeMessages(node.nodeId, sourceMessages);
const extended = {
...node,
messages,
sourceMessages: srcMsgs,
convo: srcMsgs.map(m => convoMessageToStringSafe(m) ?? '').join('\n\n'),
};
return extended;
};
export const createConvoNodeGraph = (source, sourceMessages) => {
const graph = {
nodes: (source.nodeDescriptions ?? []).map(n => getExtendedConvoNodeDescription(n, source, sourceMessages)),
entry: undefined,
};
let entryId;
for (const msg of source.messages) {
if (msg.role === convoRoles.goto && msg.gotoNodeId) {
entryId = msg.gotoNodeId;
break;
}
}
if (entryId) {
graph.entry = graph.nodes.find(n => n.nodeId = entryId);
}
return graph;
};
export const removeConvoNodeMessages = (messages) => {
let currentNodeId;
for (let i = 0; i < messages.length; i++) {
const msg = messages[i];
if (!msg) {
continue;
}
switch (msg.role) {
case convoRoles.node:
currentNodeId = msg.nodeId;
messages.splice(i, 1);
i--;
break;
case convoRoles.goto:
case convoRoles.nodeEnd:
case convoRoles.exitGraph:
currentNodeId = undefined;
messages.splice(i, 1);
i--;
break;
// always remove
case convoRoles.to:
case convoRoles.from:
case convoRoles.exit:
messages.splice(i, 1);
i--;
break;
default:
if (currentNodeId) {
messages.splice(i, 1);
i--;
}
break;
}
}
};
export const getConvoConvoNodeResults = (flat) => {
const results = [];
const stack = flat.exe.getVar(convoVars.__nodeStack);
if (!Array.isArray(stack)) {
return results;
}
for (let i = 0; i < stack.length; i++) {
const node = stack[i];
if (!node) {
continue;
}
const next = stack[i + 1];
results.push({
...node,
output: next ? next.input : flat.exe.getVar('output'),
});
}
return results;
};
export const createConvoNodeGraphResult = (completion, errorObject, error = errorObject ? getErrorMessage(errorObject) : undefined) => {
if (!completion) {
return {
success: false,
error: error ?? 'Completion unable to be created',
errorObject,
exited: false,
};
}
const last = completion.nodeResults?.[0];
if (!last || error) {
return {
success: false,
error: error ?? 'Completion contained no node results',
errorObject,
finalCompletion: completion,
exited: completion.graphExited ?? false,
};
}
return {
success: true,
result: last,
allResults: completion.nodeResults ?? [],
finalCompletion: completion,
exited: completion.graphExited ?? false,
};
};
//# sourceMappingURL=convo-node-graph-lib.js.map