@inworld/web-core
Version:
398 lines (397 loc) • 19.3 kB
JavaScript
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]);
}
}
}