UNPKG

@inworld/web-core

Version:
398 lines (397 loc) 19.3 kB
import { v4 } from 'uuid'; import { DEFAULT_USER_NAME } from '../common/constants.js'; import { InworlControlAction, } from '../common/data_structures/index.js'; import { CHAT_HISTORY_TYPE, } from '../common/data_structures/history.js'; export class InworldHistory { constructor(props) { var _a; this.audioEnabledPerConversation = {}; this.history = []; this.queue = []; this.emotions = {}; this.conversationItems = []; this.markAsApplied = (compare) => { const found = this.conversationItems.find((item) => !item.isApplied && compare(item.packet)); if (found) { found.isApplied = true; } return found; }; if (props.extension) { this.extension = props.extension; } if (props.user) { this.user = props.user; } this.scene = props === null || props === void 0 ? void 0 : props.scene; this.conversations = props.conversations; this.audioEnabled = (_a = props.audioEnabled) !== null && _a !== void 0 ? _a : false; } setAudioEnabled(conversationId, enabled) { this.audioEnabledPerConversation[conversationId] = enabled; } addOrUpdate({ characters, grpcAudioPlayer, packet, outgoing, fromHistory = false, fromHistoryCharacter, }) { var _a, _b, _c, _d, _e, _f; let historyItem; let queueItem; let needToDisplay = []; const utteranceId = packet.packetId.utteranceId; const interactionId = packet.packetId.interactionId; const conversationId = packet.packetId.conversationId; switch (true) { case packet.isAudio(): this.conversationItems.push({ packet, isApplied: false }); break; case packet.isEmotion(): this.emotions[interactionId] = packet.emotions; break; case packet.isText(): case packet.isNarratedAction(): const itemCharacters = this.findCharacters(characters, { packet }); const packetCharacters = fromHistory && fromHistoryCharacter && itemCharacters.length === 0 ? [fromHistoryCharacter] : itemCharacters; const textItem = packet.isText() ? Object.assign(Object.assign({}, this.combineTextItem(packet)), { character: packetCharacters[0], characters: packetCharacters, fromHistory }) : Object.assign(Object.assign({}, this.combineNarratedActionItem(packet, packetCharacters, this.user)), { fromHistory, conversationId }); const audioIsApplied = !!this.conversationItems.find((item) => !!item.packet.isAudio() && item.isApplied && item.packet.packetId.utteranceId === utteranceId); const audioIsEnabled = (_a = this.audioEnabledPerConversation[conversationId]) !== null && _a !== void 0 ? _a : this.audioEnabled; if (audioIsApplied || fromHistory || packet.routing.source.isPlayer || !audioIsEnabled) { historyItem = textItem; } else { queueItem = textItem; } break; case packet.isTrigger(): case packet.isTask(): historyItem = Object.assign(Object.assign({}, this.combineCustomItem(packet, outgoing)), { fromHistory, conversationId }); break; case packet.isLog(): historyItem = Object.assign(Object.assign({}, this.combineLogItem(packet)), { fromHistory }); break; case packet.isInteractionEnd(): const controlItem = Object.assign(Object.assign({}, this.combineInteractionEndItem(packet)), { conversationId }); if (this.audioEnabled && grpcAudioPlayer.hasPacketInQueue({ interactionId })) { queueItem = controlItem; } else { needToDisplay = [...this.queue].filter((item) => item.interactionId === interactionId); this.history = [...this.history, ...needToDisplay]; this.queue = this.queue.filter((item) => item.interactionId !== interactionId); historyItem = controlItem; } break; case packet.isSceneMutationRequest(): queueItem = this.combineSceneChangeItem(packet, characters); break; case packet.isSceneMutationResponse(): const sceneMutation = this.queue.findLast((item) => item.type === CHAT_HISTORY_TYPE.SCENE_CHANGE); if (sceneMutation) { const added = (_c = (_b = packet.sceneMutation.loadedCharacters) === null || _b === void 0 ? void 0 : _b.filter((l) => sceneMutation.addedCharacters.find((c) => c.resourceName === l.resourceName))) !== null && _c !== void 0 ? _c : []; sceneMutation.loadedCharacters = packet.sceneMutation.loadedCharacters; sceneMutation.addedCharacters = added; this.history = [...this.history, ...[sceneMutation]]; this.queue = this.queue.filter((item) => item.type !== CHAT_HISTORY_TYPE.SCENE_CHANGE); } return sceneMutation ? [sceneMutation] : []; case ((_d = packet.control) === null || _d === void 0 ? void 0 : _d.action) === InworlControlAction.CONVERSATION_UPDATE: const conversation = this.conversations.get(conversationId); this.queue.push(Object.assign(Object.assign({}, this.combineConversationUpdateItem(packet)), { currentCharacters: (_e = conversation.service) === null || _e === void 0 ? void 0 : _e.getCharacters() })); break; case ((_f = packet.control) === null || _f === void 0 ? void 0 : _f.action) === InworlControlAction.CONVERSATION_EVENT: const updateItem = this.queue.find((item) => item.type === CHAT_HISTORY_TYPE.CONVERSATION_UPDATE && item.conversationId === packet.packetId.conversationId); if (updateItem) { const addedPatricipants = packet.control.conversation.participants.filter((participant) => { var _a; return !((_a = updateItem.currentCharacters) === null || _a === void 0 ? void 0 : _a.find((character) => character.id === participant.name)); }); const addedCharacters = this.findCharacters(characters, { participants: addedPatricipants, }); const removedCharacters = updateItem.currentCharacters.filter((character) => { var _a; return !((_a = packet.control.conversation.participants) === null || _a === void 0 ? void 0 : _a.find((participant) => character.id === participant.name)); }); this.queue = this.queue.filter((item) => item.type !== CHAT_HISTORY_TYPE.CONVERSATION_UPDATE || item.conversationId !== packet.packetId.conversationId); if (addedCharacters.length || removedCharacters.length) { const diff = Object.assign(Object.assign({}, updateItem), { addedCharacters, removedCharacters }); this.history = [...this.history, ...[diff]]; return [diff]; } return []; } } if (historyItem) { const currentHistoryIndex = this.history.findIndex((item) => { return item.id === historyItem.id && item.type === historyItem.type; }); const item = this.convertToExtendedType(packet, historyItem); if (currentHistoryIndex >= 0) { this.history[currentHistoryIndex] = Object.assign(Object.assign({}, this.history[currentHistoryIndex]), item); } else { this.history = [...this.history, item]; } } if (queueItem) { this.queue = [ ...this.queue, this.convertToExtendedType(packet, queueItem), ]; } return historyItem ? [...needToDisplay, historyItem] : []; } update(packet) { if (packet.isText()) { let text; const currentHistoryIndex = this.history.findIndex((item) => item.id === packet.packetId.utteranceId); if (currentHistoryIndex >= 0) { text = this.combineTextItem(packet); this.history[currentHistoryIndex] = Object.assign(Object.assign({}, this.history[currentHistoryIndex]), text); return [text]; } } else if (packet.isAudio()) { this.markAsApplied((item) => item.isAudio() && item.packetId.utteranceId === packet.packetId.utteranceId); const toDisplay = this.display(packet); if (toDisplay.find((item) => item.type === CHAT_HISTORY_TYPE.INTERACTION_END)) { this.conversationItems = this.conversationItems.filter((item) => item.packet.packetId.interactionId !== packet.packetId.interactionId); } return toDisplay; } return []; } display(packet) { const types = [ CHAT_HISTORY_TYPE.ACTOR, CHAT_HISTORY_TYPE.INTERACTION_END, CHAT_HISTORY_TYPE.NARRATED_ACTION, ]; const found = this.queue.filter((item) => types.includes(item.type) && item.interactionId === packet.packetId.interactionId); const toDisplay = []; if (found.length) { for (const item of found) { if ((item.type === CHAT_HISTORY_TYPE.ACTOR && item.id === packet.packetId.utteranceId) || item.type === CHAT_HISTORY_TYPE.NARRATED_ACTION) { toDisplay.push(item); } } if (toDisplay.length + 1 === found.length && found[found.length - 1].type === CHAT_HISTORY_TYPE.INTERACTION_END) { toDisplay.push(found[found.length - 1]); } } this.queue = this.queue.filter((item) => !toDisplay.find((x) => x.id === item.id)); this.history = [...this.history, ...toDisplay]; return toDisplay; } get(conversationId) { if (!conversationId) { return this.history; } return this.history.filter((item) => item.conversationId === conversationId); } filter(props) { if (props.history) { this.history = this.history.filter(props.history); } if (props.queue) { this.queue = this.queue.filter(props.queue); } return this.history; } clear() { this.queue = []; this.history = []; } getTranscript(conversationId) { const history = this.get(conversationId); if (!history.length) { return ''; } let transcript = ''; let characterLastSpeaking = false; history.forEach((item) => { var _a, _b; const prefix = transcript.length ? '\n' : ''; switch (item.type) { case CHAT_HISTORY_TYPE.ACTOR: case CHAT_HISTORY_TYPE.NARRATED_ACTION: const isCharacter = item.source.isCharacter; const givenName = isCharacter ? item.character.displayName : this.getUserName(this.user); const emotionCode = (isCharacter && ((_b = (_a = this.emotions[item.interactionId]) === null || _a === void 0 ? void 0 : _a.behavior) === null || _b === void 0 ? void 0 : _b.code)) || ''; const emotion = emotionCode ? `(${emotionCode}) ` : ''; const text = item.type === CHAT_HISTORY_TYPE.NARRATED_ACTION ? `*${item.text}*` : item.text; transcript += characterLastSpeaking && isCharacter ? `${(transcript === null || transcript === void 0 ? void 0 : transcript[transcript.length - 1]) === ' ' ? '' : ' '}${text}` : `${prefix}${givenName}: ${emotion}${text}`; characterLastSpeaking = isCharacter; break; case CHAT_HISTORY_TYPE.TRIGGER_EVENT: case CHAT_HISTORY_TYPE.TASK_EVENT: transcript += `${prefix}>>> ${item.name}`; characterLastSpeaking = false; break; case CHAT_HISTORY_TYPE.SCENE_CHANGE: transcript += `${prefix}${prefix}>>> Now moving to ${item.to}`; characterLastSpeaking = false; break; } }); return transcript; } getUserName(user) { return (user === null || user === void 0 ? void 0 : user.fullName) || DEFAULT_USER_NAME; } combineTextItem(packet) { return { id: packet.packetId.utteranceId, isRecognizing: !packet.text.final, scene: this.scene, type: CHAT_HISTORY_TYPE.ACTOR, text: packet.text.text, date: new Date(packet.date), correlationId: packet.packetId.correlationId, conversationId: packet.packetId.conversationId, interactionId: packet.packetId.interactionId, source: packet.routing.source, }; } combineSceneChangeItem(packet, characters) { var _a, _b, _c; const removedCharacters = ((_a = packet.sceneMutation.removedCharacterIds) === null || _a === void 0 ? void 0 : _a.length) ? characters.filter((character) => { var _a; return !((_a = packet.sceneMutation.removedCharacterIds) === null || _a === void 0 ? void 0 : _a.find((id) => character.id === id)); }) : []; return Object.assign(Object.assign({ id: v4(), date: new Date(packet.date), interactionId: packet.packetId.interactionId, type: CHAT_HISTORY_TYPE.SCENE_CHANGE, source: packet.routing.source }, (((_b = packet.sceneMutation) === null || _b === void 0 ? void 0 : _b.name) && { to: packet.sceneMutation.name, description: packet.sceneMutation.description, displayName: packet.sceneMutation.displayName, })), { loadedCharacters: [], addedCharacters: ((_c = packet.sceneMutation.addedCharacterNames) === null || _c === void 0 ? void 0 : _c.map((resourceName) => ({ resourceName }))) || [], removedCharacters }); } combineConversationUpdateItem(packet) { return { id: v4(), date: new Date(packet.date), type: CHAT_HISTORY_TYPE.CONVERSATION_UPDATE, source: packet.routing.source, conversationId: packet.packetId.conversationId, }; } combineNarratedActionItem(packet, characters, user) { let text = packet.narratedAction.text; if (packet.routing.source.isPlayer) { text = text.replaceAll('{player}', this.getUserName(user)); if (characters.length) { text = text.replaceAll('{character}', characters[0].displayName); } } return { id: packet.packetId.utteranceId, date: new Date(packet.date), scene: this.scene, character: characters[0], characters, interactionId: packet.packetId.interactionId, conversationId: packet.packetId.conversationId, source: packet.routing.source, type: CHAT_HISTORY_TYPE.NARRATED_ACTION, text, }; } combineCustomItem(packet, outgoing) { return Object.assign({ id: packet.packetId.utteranceId, scene: this.scene, date: new Date(packet.date), interactionId: packet.packetId.interactionId, conversationId: packet.packetId.conversationId, correlationId: packet.packetId.correlationId, outgoing, source: packet.routing.source }, (packet.isTrigger() ? { type: CHAT_HISTORY_TYPE.TRIGGER_EVENT, name: packet.trigger.name, parameters: packet.trigger.parameters, } : { type: CHAT_HISTORY_TYPE.TASK_EVENT, name: packet.task.name, parameters: packet.task.parameters, })); } combineInteractionEndItem(packet) { return { id: v4(), date: new Date(packet.date), interactionId: packet.packetId.interactionId, conversationId: packet.packetId.conversationId, scene: this.scene, source: packet.routing.source, type: CHAT_HISTORY_TYPE.INTERACTION_END, }; } combineLogItem(packet) { return { id: packet.packetId.utteranceId, scene: this.scene, conversationId: packet.packetId.conversationId, date: new Date(packet.date), interactionId: packet.packetId.interactionId, level: packet.log.level, metadata: packet.log.metadata, details: packet.log.details, source: packet.routing.source, text: packet.log.text, type: CHAT_HISTORY_TYPE.LOG_EVENT, }; } convertToExtendedType(packet, item) { var _a, _b; return ((_b = (_a = this.extension) === null || _a === void 0 ? void 0 : _a.historyItem) === null || _b === void 0 ? void 0 : _b.call(_a, packet, item)) || item; } findCharacters(characters, props) { var _a; const { packet, participants = [] } = props; const byId = characters.reduce((acc, character) => { acc[character.id] = character; return acc; }, {}); if (!!packet) { if (packet.routing.source.isCharacter) { return byId[packet.routing.source.name] ? [byId[packet.routing.source.name]] : []; } if (packet.routing.targets.length) { return packet.routing.targets .filter((x) => x.isCharacter && byId[x.name]) .map((x) => byId[x.name]); } const conversation = this.conversations.get(packet.packetId.conversationId); return ((_a = conversation === null || conversation === void 0 ? void 0 : conversation.service) === null || _a === void 0 ? void 0 : _a.getCharacters()) || []; } else { return participants .filter((x) => x.isCharacter && byId[x.name]) .map((x) => byId[x.name]); } } }