Nicola Zambello
Frontend developer. GitHub addicted, curious, perfectionist.
Formazione Client
Lead Frontend Developer
nzambello
Core team
Creiamo un layout web
Personalizziamolo
Embeddiamolo
<style>
:root, .memori.memori-widget,
#headlessui-portal-root, memori-client {
--memori-primary: rgb(255, 0, 0);
--memori-primary-text: #fff;
--memori-inner-content-pad: 1rem;
--memori-inner-bg: #fff;
--memori-chat-bubble-bg: #ffffff60;
--memori-text-color: #000;
--memori-button-bg: #fff;
--memori-button-text: #000;
--memori-button-padding: 0.5rem 1.5rem;
--memori-button-border-color: #d9d9d9;
--memori-button-radius: 15px;
--memori-button-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.02);
--memori-blur-background: 5px;
--memori-drawer--width: 100%;
--memori-drawer--width--md: 80%;
--memori-drawer--width--lg: 60%;
--memori-modal--width: 100%;
--memori-modal--width--md: 80%;
--memori-error-color: #ff4d4f;
}
</style>
Personalizziamolo dinamicamente
<memori-client
memoriName="Memori"
ownerUserName="nunziofiore"
tenantID="app.memorytwin.com"
showShare="true"
apiURL="https://backend.memori.ai"
baseURL="https://app.memorytwin.com"
uiLang="IT"
spokenLang="IT"
tag="🍻"
pin="123456"
context="VAR1:VALUE1,VAR2:VALUE2"
initialQuestion="Initial question"
/>
Parametri aggiuntivi avanzati
Layout disponibili
FULLPAGE
TOTEM
CHAT
<memori-client
memoriName="Memori"
ownerUserName="nunziofiore"
tenantID="app.memorytwin.com"
showShare="true"
apiURL="https://backend.memori.ai"
baseURL="https://app.memorytwin.com"
uiLang="it"
tag="🍻"
pin="123456"
context="VAR1:VALUE1,VAR2:VALUE2"
initialQuestion="Initial question"
layout="CHAT"
/>
Create dallo stesso agente, più integrazioni.
Pensando ad un caso d'uso, create un widget e pensate a persone o contesti da associarvi
INIT
SESSIONE
TEXT
ENTERED
sessionID
memoriID
tag, pin
initialContext
password
state
hints
media
emission
sessionID
text
CURRENT
STATE
sessionID
(la sessione scade)
birthDate
Postman
Note su ID, nomi e pagina pubblica
GET https://backend.memori.ai/api/v2/Memori/TENANT/USER_NAME/MEMORI_NAME
Otteniamo i dati di un Memori (Twin)
GET https://backend.memori.ai/api/v2/MemoriByID/TENANT/USER_ID/MEMORI_ID
POST https://engine.memori.ai/memori/v2/Session
Iniziamo una sessione
{ "memoriId": "ENGINE_MEMORI_ID", "birthDate": "YYYY-MM-DDHH:mm:ss.dddZ" }
POST https://engine.memori.ai/memori/v2/Session
Iniziamo una sessione
con parametri aggiuntivi
{ "memoriId": "ENGINE_MEMORI_ID" "tag": "🍻", "pin": "123456", "initialQuestion": "Initial question", "initialContextVars": "VAR1:VALUE1", "birthDate": "YYYY-MM-DDHH:mm:ss.dddZ" }
DELETE
https://engine.memori.ai/memori/v2/Session/SESSION_ID
Altre operazioni su una sessione
GET
https://engine.memori.ai/memori/v2/Session/SESSION_ID
POST
https://engine.memori.ai/memori/v2/TextEnteredEvent/SESSION_ID
{
"text": "INSERT_TEXT_HERE"
}
Inviamo del testo
POST
https://engine.memori.ai/memori/v2/TagChangedEvent/SESSION_ID
Altri eventi accettati
(occhio agli accepts)
POST
https://engine.memori.ai/memori/v2/PlaceChangedEvent/SESSION_ID
POST
https://engine.memori.ai/memori/v2/TimeoutEvent/SESSION_ID
Es: https://assets.memori.ai/api/v2/asset/ea79664c-dd90-4d19-a956-a090e6cbdc4b.jpg
Nota sugli asset
I media sono associati ai contenuti sono protetti, per accedervi serve il sessionID
⇓
https://assets.memori.ai/api/v2/asset/ea79664c-dd90-4d19-a956-a090e6cbdc4b.jpg/SESSION_ID
SDK TypeScript
yarn add @memori.ai/memori-api-client
const { sessionID, currentState, ...resp } = await memori.initSession(
'768b9654-e781-4c3c-81fa-ae1529d1bfbe',
);
const { currentState: dialogState, ...resp } = await memori.postTextEnteredEvent(
sessionID,
'Ciao, Memori!'
);
// utility
memori.asset.getResourceUrl({
type: 'avatar',
resourceURI: '768b9654-e781-4c3c-81fa-ae1529d1bfbe.png',
mediaType: 'image/png',
sessionId: 'be2e4a44-890b-483b-a26a-f6e122f36e2b',
});
Con l'uso di Postman o un client REST a scelta,
avviare una sessione e proseguire una conversazione.
Messaggio di benvenuto e risposte di default
Gestione suggerimenti e timeout
Gestione contesti
Snippet eseguibili
Snippet eseguibili
<div id="extension"></div>
Esiste un div con id "extension" nella colonna dell'avatar, usabile per aggiungere markup in integrazioni o estensioni della chat, magari tramite snippet eseguibili
Estensioni
const state = getMemoriState();
const sessionID = state.sessionID;
const extension = document.getElementById('extension');
extension.innerHTML = '';
const wrapper = document.createElement('div');
wrapper.id = 'memori-media-wrapper';
extension.appendChild(wrapper);
const images = state.media.filter((m) => m.mimeType.startsWith('image'));
images.forEach((m) => {
const image = document.createElement('img');
image.src = `${m.url}/${sessionID}`;
image.alt = m.name || '';
wrapper.appendChild(image);
});
Ecco un esempio dove è utilizzato per nascondere le immagini dalla chat e invece mostrarle in griglia sopra all'avatar
Prendiamo lo stato corrente con getMemoriState() e quindi le immagini da esso con un filtro per MIMEType, da quelle generiamo gli elementi del DOM
NOTA: per ripulire da eventuali altri elementi, prima svuotiamo il div
const styles = document.createElement('style');
styles.innerHTML = `
#memori-media-wrapper {
display: flex;
flex-wrap: wrap;
position: absolute;
top: 2rem;
z-index: 1000;
}
#memori-media-wrapper img {
width: 100%;
padding: 0.5rem;
}
.memori-media-widget {
display: none;
}
`;
extension.appendChild(styles);
Aggiungiamo del CSS per mostrarlo bene e appendiamo il tutto
document.addEventListener('MemoriEndSpeak', () => {
extension.innerHTML = '';
})
Ripuliamo #extension al prossimo evento, in modo da non creare conflitti
Gestione intenti
const startConversation = () => {
fetch("https://engine.memori.ai/memori/v2/session", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
memoriId: "1afe57c6-1b69-4a61-96ea-52bf7b8d158e",
}),
})
.then((response) => response.json())
.then((result) => {
state = result;
if (state.currentState.emission)
console.log(`Nunzio: ${state.currentState.emission}`);
handleConversation();
})
.catch((error) => console.log("error", error));
};
const sendMessage = (question) => {
fetch(
`https://engine.memori.ai/memori/v2/TextEnteredEvent/${state.sessionID}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
text: question,
}),
}
)
.then((response) => response.json())
.then((result) => {
state = result;
if (state.currentState.emission)
console.log(`Nunzio: ${state.currentState.emission}`);
})
.catch((error) => console.log("error", error));
}
SDK TypeScript
yarn add @memori.ai/memori-api-client
const { sessionID, currentState, ...resp } = await memori.initSession(
'768b9654-e781-4c3c-81fa-ae1529d1bfbe',
);
const { currentState: dialogState, ...resp } = await memori.postTextEnteredEvent(
sessionID,
'Ciao, Memori!'
);
// utility
memori.asset.getResourceUrl({
type: 'avatar',
resourceURI: '768b9654-e781-4c3c-81fa-ae1529d1bfbe.png',
mediaType: 'image/png',
sessionId: 'be2e4a44-890b-483b-a26a-f6e122f36e2b',
});
Libreria React
yarn add @memori.ai/memori-react
import Memori from '@memori.ai/memori-react';
import '@memori.ai/memori-react/dist/styles.css';
const App = () => (
<Memori
memoriName="Memori"
ownerUserName="nunziofiore"
tenantID="app.memorytwin.com"
apiURL="https://backend.memori.ai"
baseURL="https://app.memorytwin.com"
uiLang="it"
showShare
height="100vh"
/>
);
WebComponent
<script
type="module"
src="https://esm.run/@memori.ai/memori-webcomponent/dist/memori-webcomponent.js"
></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@memori.ai/memori-react/dist/styles.min.css"
/>
<memori-client
memoriName="Memori"
ownerUserName="nunziofiore"
tenantID="app.memorytwin.com"
showShare
apiURL="https://backend.memori.ai"
baseURL="https://app.memorytwin.com"
uiLang="it"
height="100vh"
/>
let dialogState = getMemoriState();
let sessionID = getMemoriState().sessionID;
// in case you have multiple widgets on the same page
let dialogState = getMemoriState(myWidgetIntegrationId);
typeMessage('Hello World!')
// waits for previous message to be read, default: true
const waitForPrevious = true
// message is not visible to the user, only the response is, default: false
const hidden = true
typeMessage('Hello World!', waitForPrevious, hidden)
typeMessageHidden('Hello World!', waitForPrevious)
// alias to
typeMessage('Hello World!', waitForPrevious, true)
typeBatchMessages([
{
message: 'percorso libero',
hidden: true,
waitForPrevious: true,
useLoaderTextAsMsg: true,
typingText: 'Preparo il cartamodello...',
},
{
message: 'imposta lo scollo a v',
hidden: true,
waitForPrevious: true,
useLoaderTextAsMsg: true,
typingText: 'Cucio lo scollo come preferisci...'
},
...
])
Custom layout:
memori-ai/memori-react#custom-layout
Component overrides:
onStateChange
Creare un'applicazione React e installare il componente
Creare un layout custom e mostrare lo stato e le variazioni
Pensare ad un'integrazione di sistemi e ragioniamoci insieme
TwinCreator VR
Ora o quando volete
By Nicola Zambello