@plteam/chat-ui
Version:
CUI Kit is a free and open-source library for creating AI assistant chat interfaces, built with React, Material UI, and TypeScript
121 lines (119 loc) • 4.85 kB
JavaScript
import { ObservableReactValue } from '../utils/observers/ObservableReactValue';
import { arrayLast } from '../utils/arrayUtils/arrayLast';
import { isDefined } from '../utils/isDefined';
import { ThreadBranchMapManager } from './ThreadBranchMapManager';
const rootMessageHash = 'rootMessage';
export class ThreadMessages {
allMessages = new ObservableReactValue([]);
currentMessages = new ObservableReactValue([]);
internalMessageTransformer;
getCurrentBranchFn;
branchMapManager = new ThreadBranchMapManager();
get allMessagesArray() {
return this.allMessages.value;
}
// Remember which branch we stopped at, so that upon re-entering the thread, reopens it
_lastMessage;
_callbackInitiated = false;
init = (enableBranches) => {
if (this._callbackInitiated)
return;
const map = this._createNewMap(this.allMessagesArray);
this._updateBranch(map, this._lastMessage, enableBranches);
// reaction to adding to the array of all messages to update the thread
this.allMessages.subscribe(() => {
const map = this._createNewMap(this.allMessagesArray);
this._updateBranch(map, arrayLast(this.allMessagesArray), enableBranches);
});
};
changeBranchesStatus = (enableBranches) => {
const map = this._createNewMap(this.allMessagesArray);
this._updateBranch(map, this._lastMessage, enableBranches);
};
handleChangeBranch = (message) => {
const parentId = message.parentId ?? rootMessageHash;
const map = this._createNewMap(this.allMessagesArray);
map.set(parentId, { messages: [message] });
// console.log(message, messagesParentMap);
this._updateBranch(map, message, true);
};
_updateBranch = (map, startFrom, enableBranches) => {
if (enableBranches !== true) {
this.currentMessages.setValue(this.allMessagesArray);
return;
}
if (!startFrom)
startFrom = this._lastMessage;
let newBranch = [];
if (this.getCurrentBranchFn) {
newBranch = this.getCurrentBranchFn(startFrom ? this.internalMessageTransformer(startFrom) : undefined)
.map(v => this.allMessagesArray.find(m => m.id === v.id))
.filter(isDefined);
}
else {
const rootMessages = startFrom ? [startFrom] : map.get(rootMessageHash)?.messages ?? [];
const branches = [];
for (const rootMessage of rootMessages) {
branches.push(this._getBranchRecursive(rootMessage, map));
}
const topItems = branches.map(b => arrayLast(b))
.filter(isDefined);
const topItem = arrayLast(topItems);
/*console.log(
branches.length,
sortByDesc(branches[0]?.slice() ?? [], 'time').map((v) => ({
text: v.text,
pid: v.parentId,
id: v.id,
time: v.time - 1734000000,
}))
);
console.log(topItem, createTree(topItem, thread.messages));*/
newBranch = topItem?.parentId ? this._createTree(topItem, this.allMessagesArray) : this.allMessagesArray;
}
this.currentMessages.setValue(newBranch);
this._lastMessage = arrayLast(newBranch);
return this._lastMessage;
};
_getBranchRecursive = (parent, map) => {
const branch = [parent];
const parentMap = map.get(parent.id);
if (parentMap) {
// все ради цикла :)
const darkness = parentMap.messages;
for (const child of darkness) {
branch.push(...this._getBranchRecursive(child, map));
}
}
return branch;
};
_createTree = (lastItem, messages) => {
const branch = [];
let nextItem = lastItem;
while (nextItem) {
branch.push(nextItem);
if (nextItem.parentId) {
nextItem = messages.find(m => m.id === nextItem.parentId);
}
else {
nextItem = undefined;
}
}
return branch.reverse();
};
_createNewMap = (messages) => {
// not needed, since we don't use the map if getCurrentBranchFn provided
if (this.getCurrentBranchFn)
return new Map();
return this.branchMapManager.createMap(messages);
};
push = (...newMessages) => {
this.allMessages.setValue([
...this.allMessages.value,
...newMessages,
]);
};
get length() {
return this.allMessages.value.length;
}
}