UNPKG

@convo-lang/convo-lang

Version:
121 lines 4.7 kB
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