@convo-lang/convo-lang
Version:
The language of AI
121 lines • 4.7 kB
JavaScript
import { aryRemoveItem, getErrorMessage } from "@iyio/common";
import { BehaviorSubject, Subject } from "rxjs";
import { Conversation } from "./Conversation.js";
import { createConvoNodeGraphResult } from "./convo-node-graph-lib.js";
export class ConvoNodeGraphCtrl {
get onStepComplete() { return this._onStepComplete; }
get resultSubject() { return this._result; }
get result() { return this._result.value; }
addStepCompletionListener(listener) {
this.onStepCompleteCallbacks.push(listener);
return () => {
aryRemoveItem(this.onStepCompleteCallbacks, listener);
};
}
removeStepCompletionListener(listener) {
aryRemoveItem(this.onStepCompleteCallbacks, listener);
}
get stateSubject() { return this._state; }
get state() { return this._state.value; }
get stepIndexSubject() { return this._stepIndex; }
get stepIndex() { return this._stepIndex.value; }
constructor({ maxSteps, convoOptions = {}, convo, initCtrl, }) {
this._onStepComplete = new Subject();
this._result = new BehaviorSubject(null);
this.onStepCompleteCallbacks = [];
this._state = new BehaviorSubject('ready');
this._stepIndex = new BehaviorSubject(0);
this._isDisposed = false;
convoOptions = {
...convoOptions,
disableGraphSummary: convoOptions.disableGraphSummary ?? true,
};
this.conversation = (typeof convo === 'string') ?
new Conversation({
...convoOptions,
initConvo: convo
}) :
convo;
this.options = {
maxSteps,
convoOptions,
};
initCtrl?.(this);
}
get isDisposed() { return this._isDisposed; }
dispose() {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
}
shouldContinue() {
return (!this._isDisposed &&
!this.conversation.isDisposed &&
(this.options.maxSteps === undefined || this.options.maxSteps > this.stepIndex));
}
/**
* Runs a convo node graph by calling `Conversation.completeAsync` in a loop until the graph
* exists. `runAsync` is guaranteed to not throw an error, the returned result object will
* contain an error if an error is thrown inside the function.
*/
async runAsync() {
return await (this.runPromise ?? (this.runPromise = this._runAsync()));
}
async _runAsync() {
let result;
let lastCompletion;
try {
this._state.next('running');
while (this.shouldContinue()) {
lastCompletion = undefined;
const completion = await this.conversation.completeAsync({
appendNextGoto: true,
disableGraphSummary: this.options.convoOptions?.disableGraphSummary
});
lastCompletion = completion;
try {
this._onStepComplete.next(completion);
if (this.onStepCompleteCallbacks.length) {
await Promise.all(this.onStepCompleteCallbacks.map(async (c) => {
await c(completion);
}));
}
}
catch (ex) {
const msg = 'Convo node graph stopped due to completion callback error';
console.error(msg, ex);
result = createConvoNodeGraphResult(completion, ex, `${msg}: ${getErrorMessage(ex)}`);
break;
}
if (!completion.nextNodeId || completion.graphExited) {
result = createConvoNodeGraphResult(completion);
break;
}
}
}
catch (ex) {
const msg = 'Convo node graph stopped due to an error';
const errorMsg = `${msg}: ${getErrorMessage(ex)}`;
console.error(msg, ex);
result = createConvoNodeGraphResult(lastCompletion, ex, errorMsg);
}
if (!result) {
result = createConvoNodeGraphResult(null, null, 'ConvoNodeGraphCtrl existed early');
}
try {
this._result.next(result);
}
catch (ex) {
console.error('Error while setting ConvoNodeGraphCtrl result', ex);
}
try {
this._state.next('stopped');
}
catch (ex) {
console.error('Error while setting ConvoNodeGraphCtrl state to stopped', ex);
}
return result;
}
}
//# sourceMappingURL=ConvoNodeGraphCtrl.js.map