UNPKG

@iyio/convo-lang

Version:

A conversational language.

400 lines 13.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConversationUiCtrl = void 0; const common_1 = require("@iyio/common"); const rxjs_1 = require("rxjs"); const Conversation_1 = require("./Conversation"); const LocalStorageConvoDataStore_1 = require("./LocalStorageConvoDataStore"); const convo_lang_ui_lib_1 = require("./convo-lang-ui-lib"); const convo_lib_1 = require("./convo-lib"); class ConversationUiCtrl { get lastCompletionSubject() { return this._lastCompletion; } get lastCompletion() { return this._lastCompletion.value; } get currentTaskSubject() { return this._currentTask; } get currentTask() { return this._currentTask.value; } get templateSubject() { return this._template; } get template() { return this._template.value; } set template(value) { if (value == this._template.value) { return; } this._template.next(value); } ; get removeDanglingUserMessagesSubject() { return this._removeDanglingUserMessages; } get removeDanglingUserMessages() { return this._removeDanglingUserMessages.value; } set removeDanglingUserMessages(value) { if (value == this._removeDanglingUserMessages.value) { return; } this._removeDanglingUserMessages.next(value); } get convoSubject() { return this._convo; } get convo() { return this._convo.value; } get showSourceSubject() { return this._showSource; } /** * If true the source convo-lang syntax should be displayed to the user */ get showSource() { return this._showSource.value; } set showSource(value) { if (value == this._showSource.value) { return; } this._showSource.next(value); } get editorModeSubject() { return this._editorMode; } get editorMode() { return this._editorMode.value; } set editorMode(value) { if (value == this._editorMode.value) { return; } this._editorMode.next(value); } get showSystemMessagesSubject() { return this._showSystemMessages; } /** * If true the system messages should be displayed to the user */ get showSystemMessages() { return this._showSystemMessages.value; } set showSystemMessages(value) { if (value == this._showSystemMessages.value) { return; } this._showSystemMessages.next(value); } get showFunctionsSubject() { return this._showFunctions; } /** * If true function calls should be displayed to the user */ get showFunctions() { return this._showFunctions.value; } set showFunctions(value) { if (value == this._showFunctions.value) { return; } this._showFunctions.next(value); } get enabledSlashCommandsSubject() { return this._enabledSlashCommands; } /** * If messages appended to the conversation using the appendUiMessage will be checked for messages * starting with a forward slash and be interpreted as a command. */ get enabledSlashCommands() { return this._enabledSlashCommands.value; } set enabledSlashCommands(value) { if (value == this._enabledSlashCommands.value) { return; } this._enabledSlashCommands.next(value); } get themeSubject() { return this._theme; } get theme() { return this._theme.value; } set theme(value) { if (value == this._theme.value) { return; } this._theme.next(value); } get mediaQueueSubject() { return this._mediaQueue; } get mediaQueue() { return this._mediaQueue.value; } get collapsedSubject() { return this._collapsed; } /** * Often used to indicate if the conversation is display in a collapsed state */ get collapsed() { return this._collapsed.value; } set collapsed(value) { if (value == this._collapsed.value) { return; } this._collapsed.next(value); } get onClear() { return this._onClear; } constructor({ id, autoLoad, convo, initConvo, convoOptions, template, autoSave = false, removeDanglingUserMessages = false, store = 'localStorage', enableSlashCommand = false, } = {}) { this._lastCompletion = new rxjs_1.BehaviorSubject(null); this.tasks = []; this._currentTask = new rxjs_1.BehaviorSubject(null); this._convo = new rxjs_1.BehaviorSubject(null); this._showSource = new rxjs_1.BehaviorSubject(false); this._editorMode = new rxjs_1.BehaviorSubject('code'); this._showSystemMessages = new rxjs_1.BehaviorSubject(false); this._showFunctions = new rxjs_1.BehaviorSubject(false); this._theme = new rxjs_1.BehaviorSubject({}); this._mediaQueue = new rxjs_1.BehaviorSubject([]); this._collapsed = new rxjs_1.BehaviorSubject(true); this._onClear = new rxjs_1.Subject(); this.componentRenderers = {}; this._isDisposed = false; this.convoTaskCount = 0; this.convoTaskSub = null; this.isAutoSaving = false; this.autoSaveRequested = false; this.messageRenderers = []; this.id = id ?? (0, common_1.shortUuid)(); this._removeDanglingUserMessages = new rxjs_1.BehaviorSubject(removeDanglingUserMessages); this._enabledSlashCommands = new rxjs_1.BehaviorSubject(enableSlashCommand); this.convoOptions = convoOptions; this.initConvoCallback = initConvo; this._template = new rxjs_1.BehaviorSubject(template); this.autoSave = autoSave; this.store = store === 'localStorage' ? new LocalStorageConvoDataStore_1.LocalStorageConvoDataStore() : store; if (id !== undefined && autoLoad) { if (convo) { this.setConvo(convo); } this.loadAsync(); } else { this.setConvo(convo ?? this.createConvo(true)); } } get isDisposed() { return this._isDisposed; } dispose() { if (this._isDisposed) { return; } this._isDisposed = true; this._currentTask.next('disposed'); if (this.convoTaskSub) { this.convoTaskSub.unsubscribe(); this.convoTaskSub = null; } } initConvo(convo, options) { if (this.initConvoCallback) { this.initConvoCallback(convo); } if (options.appendTemplate && this.template) { convo.append(this.template); } } createConvo(appendTemplate) { const convo = new Conversation_1.Conversation(this.convoOptions); this.initConvo(convo, { appendTemplate }); return convo; } async loadAsync() { if (this.isDisposed || this.currentTask || !this.store?.loadConvo) { return false; } this.pushTask('loading'); try { const str = await this.store?.loadConvo?.(this.id); if (this.isDisposed) { return false; } const convo = this.createConvo(str ? false : true); if (str) { convo.append(str); } this.setConvo(convo); } finally { this.popTask('loading'); } return true; } clear() { if (this.isDisposed || this.currentTask) { return false; } this.setConvo(this.createConvo(true)); if (this.autoSave) { this.queueAutoSave(); } this._onClear.next(); return true; } pushTask(task) { this.tasks.push(task); if (this._currentTask.value !== task) { this._currentTask.next(task); } } popTask(task) { if (this.isDisposed) { return; } const i = this.tasks.lastIndexOf(task); if (i === -1) { console.error(`ConversationUiCtrl.popTask out of sync. (${task}) not in current list`); return; } this.tasks.splice(i, 1); const next = this.tasks[this.tasks.length - 1] ?? null; if (this._currentTask.value !== next) { this._currentTask.next(next); } } setConvo(convo) { if (this.convoTaskSub) { this.convoTaskSub.unsubscribe(); this.convoTaskSub = null; } while (this.tasks.includes('completing')) { this.popTask('completing'); } this.convoTaskSub = convo.activeTaskCountSubject.subscribe(n => { if (n === 1) { if (!this.tasks.includes('completing')) { this.pushTask('completing'); } } else if (n === 0) { if (this.tasks.includes('completing')) { this.popTask('completing'); } } }); this._convo.next(convo); } replace(convo) { if (this.isDisposed || this.currentTask) { return false; } if (this.removeDanglingUserMessages) { convo = (0, convo_lib_1.removeDanglingConvoUserMessage)(convo); } const c = this.createConvo(false); try { c.append(convo); } catch { return false; } this.setConvo(c); if (this.autoSave) { this.queueAutoSave(); } return true; } async replaceAndCompleteAsync(convo) { if (!this.replace(convo)) { return false; } if (this.currentTask) { return false; } await this.convo?.completeAsync(); this._lastCompletion.next(this.convo?.convo ?? null); if (this.autoSave) { this.queueAutoSave(); } return true; } isSlashCommand(message) { return cmdReg.test(message); } async appendUiMessageAsync(message) { if (this.isDisposed) { return false; } if (cmdReg.test(message)) { if (!this._enabledSlashCommands.value) { return false; } message = message.trim(); switch (message) { case '/source': this.showSource = this.editorMode === 'code' ? !this.showSource : true; this.editorMode = 'code'; break; case '/vars': this.showSource = this.editorMode === 'vars' ? !this.showSource : true; this.editorMode = 'vars'; break; case '/flat': this.showSource = this.editorMode === 'flat' ? !this.showSource : true; this.editorMode = 'flat'; break; case '/tree': this.showSource = this.editorMode === 'tree' ? !this.showSource : true; this.editorMode = 'tree'; break; case '/system': this.showSystemMessages = !this.showSystemMessages; break; case '/function': this.showFunctions = !this.showFunctions; break; case '/clear': this.clear(); break; } return 'command'; } const convo = this.convo; if (!convo || convo.isCompleting || this.currentTask) { return false; } if (this.mediaQueue.length) { message += '\n\n' + this.mediaQueue.map(i => { const url = (0, convo_lang_ui_lib_1.getConvoPromptMediaUrl)(i, 'prompt'); if (!url) { return ''; } return `![](${encodeURI(url)})`; }).join('\n'); this._mediaQueue.next([]); } if (message.trim()) { convo.appendUserMessage(message); } await convo.completeAsync(); this._lastCompletion.next(convo.convo ?? null); if (this.autoSave) { this.queueAutoSave(); } return true; } queueMedia(media) { this._mediaQueue.next([...this._mediaQueue.value, (typeof media === 'string') ? { url: media } : media]); } dequeueMedia(media) { if (typeof media === 'string') { const match = this._mediaQueue.value.find(m => m.url === media); if (!match) { return false; } media = match; } if (!this._mediaQueue.value.includes(media)) { return false; } this._mediaQueue.next((0, common_1.aryDuplicateRemoveItem)(this._mediaQueue.value, media)); return true; } queueAutoSave() { if (this.isDisposed || !this.store) { return; } if (this.isAutoSaving) { this.autoSaveRequested = true; return; } this._autoSaveAsync(); } async _autoSaveAsync() { if (this.isAutoSaving || !this.store) { return; } this.isAutoSaving = true; do { try { await this.store?.saveConvo?.(this.id, this.convo?.convo ?? ''); } catch (ex) { console.error('ConversationUiCtrl auto save failed', ex); } } while (this.autoSaveRequested && !this.isDisposed); } renderMessage(message, index) { for (const r of this.messageRenderers) { const v = r(message, index, this); if (v !== undefined && v !== null) { return v; } } return undefined; } } exports.ConversationUiCtrl = ConversationUiCtrl; const cmdReg = /^\s*\//; //# sourceMappingURL=ConversationUiCtrl.js.map