1
1
mirror of https://github.com/leon-ai/leon.git synced 2024-09-19 22:07:10 +03:00

feat: communicate suggestions to the client

This commit is contained in:
louistiti 2022-06-19 13:47:43 +08:00
parent 192dd0a87a
commit 4b5a883510
No known key found for this signature in database
GPG Key ID: 7ECA3DD523793FE6
8 changed files with 88 additions and 23 deletions

View File

@ -46,6 +46,11 @@ table {
outline: none;
}
:root {
--black-color: #151718;
--white-color: #FFF;
}
a {
color: inherit;
}
@ -55,8 +60,8 @@ ul li {
}
body {
color: #FFF;
background-color: #151718;
color: var(--white-color);
background-color: var(--black-color);
font-family: 'Open Sans', sans-serif;
font-weight: 400;
}
@ -107,10 +112,10 @@ footer {
input {
text-align: center;
color: #FFF;
color: var(--white-color);
width: 100%;
border: none;
border-bottom: 2px solid #FFF;
border-bottom: 2px solid var(--white-color);
background: none;
font-weight: 400;
font-size: 4em;
@ -118,7 +123,7 @@ input {
}
small {
color: #FFF;
color: var(--white-color);
font-size: .7em;
}
@ -139,7 +144,7 @@ small {
top: 10%;
height: 50%;
overflow-y: auto;
border: 2px solid #FFF;
border: 2px solid var(--white-color);
border-radius: 12px;
}
#feed::-webkit-scrollbar {
@ -172,7 +177,7 @@ small {
border-radius: 50%;
width: 10px;
height: 10px;
background-color: #FFF;
background-color: var(--white-color);
transform: scale(1);
}
#is-typing .circle:nth-child(1) {
@ -181,7 +186,7 @@ small {
}
#is-typing .circle:nth-child(2) {
animation: typing .2s .2s linear infinite alternate;
background-color: #FFF;
background-color: var(--white-color);
}
#is-typing .circle:nth-child(3) {
animation: typing .2s linear infinite alternate;
@ -215,12 +220,12 @@ small {
}
#feed .me .bubble {
background-color: #1C75DB;
color: #FFF;
color: var(--white-color);
right: 0;
}
#feed .leon .bubble {
background-color: #EEE;
color: #151718;
color: var(--black-color);
}
@keyframes fadeIn {
100% {
@ -228,10 +233,35 @@ small {
}
}
#suggestions-container {
position: absolute;
z-index: 10;
width: 100%;
bottom: 36%;
display: flex;
justify-content: flex-end;
column-gap: 8px;
overflow-x: auto;
}
.suggestion {
border: 1px solid var(--white-color);
background-color: transparent;
color: var(--white-color);
border-radius: 8px;
padding: 2px 8px;
font-size: inherit;
cursor: pointer;
transition: background-color .2s, color .2s;
}
.suggestion:hover {
color: var(--black-color);
background-color: var(--white-color);
}
#input-container {
position: absolute;
width: 100%;
bottom: 25%;
bottom: 22%;
}
#mic-container {
@ -244,7 +274,7 @@ small {
font-style: italic;
}
button {
#mic-button {
position: absolute;
border: none;
cursor: pointer;
@ -256,16 +286,16 @@ button {
mask-image: url(../img/mic.svg);
transition: background-color .2s;
}
button:not(.enabled) {
#mic-button:not(.enabled) {
margin-left: -26px;
}
button:hover {
background-color: #FFF;
#mic-button:hover {
background-color: var(--white-color);
}
button.enabled {
#mic-button.enabled {
background-color: #00E676;
}
button.enabled + #sonar {
#mic-button.enabled + #sonar {
width: 26px;
height: 26px;
border-radius: 50%;

View File

@ -14,6 +14,7 @@
You can start to interact with Leon, don't be shy.
</p>
</div>
<div id="suggestions-container"></div>
<div id="is-typing">
<div class="circle"></div>
<div class="circle"></div>
@ -21,7 +22,7 @@
</div>
<div id="input-container">
<div id="mic-container">
<button></button>
<button id="mic-button"></button>
<div id="sonar"></div>
</div>
<label for="utterance"></label>

View File

@ -2,9 +2,10 @@ import { io } from 'socket.io-client'
import Chatbot from './chatbot'
export default class Client {
constructor (client, serverUrl, input, res) {
constructor (client, serverUrl, input, suggestionsContainer, res) {
this.client = client
this._input = input
this._suggestionContainer = suggestionsContainer
this.serverUrl = serverUrl
this.socket = io(this.serverUrl)
this.history = localStorage.getItem('history')
@ -43,6 +44,13 @@ export default class Client {
this.chatbot.receivedFrom('leon', data)
})
this.socket.on('suggest', (data) => {
data.forEach((suggestion) => {
this._suggestionContainer.innerHTML
+= `<button class="suggestion">${suggestion}</button>`
})
})
this.socket.on('is-typing', (data) => {
this.chatbot.isTyping('leon', data)
})

View File

@ -26,10 +26,11 @@ document.addEventListener('DOMContentLoaded', () => {
console.error(err.response.error.message)
} else {
const input = document.querySelector('#utterance')
const mic = document.querySelector('button')
const mic = document.querySelector('#mic-button')
const v = document.querySelector('#version small')
const logger = document.querySelector('#logger small')
const client = new Client(config.app, serverUrl, input, res.body)
const suggestionsContainer = document.querySelector('#suggestions-container')
const client = new Client(config.app, serverUrl, input, suggestionsContainer, res.body)
let rec = { }
let chunks = []
let sLogger = ' enabled, thank you.'

View File

@ -161,9 +161,14 @@ class Brain {
const { nluDataFilePath, classification: { action: actionName } } = obj
const { actions } = JSON.parse(fs.readFileSync(nluDataFilePath, 'utf8'))
const action = actions[actionName]
const { type: actionType } = action
const { type: actionType, suggestions } = action
const nextAction = action.next_action ? actions[action.next_action] : null
// Send suggestions to the client if this action does not contain an action loop
if (suggestions && !action.loop) {
this._socket.emit('suggest', suggestions)
}
if (actionType === 'logic') {
/**
* "Logic" action skill execution

View File

@ -188,7 +188,9 @@ class Nlu {
const { actions } = JSON.parse(fs.readFileSync(nluDataFilePath, 'utf8'))
const action = actions[this.nluResultObj.classification.action]
const { name: expectedItemName, type: expectedItemType } = action.loop.expected_item
const {
name: expectedItemName, type: expectedItemType
} = action.loop.expected_item
let hasMatchingEntity = false
let hasMatchingResolver = false
@ -244,6 +246,11 @@ class Nlu {
// Break the action loop and prepare for the next action if necessary
if (processedData.core?.isInActionLoop === false) {
// Send suggestions to the client only at the end of the action loop
if (action.suggestions) {
this.brain.socket.emit('suggest', action.suggestions)
}
this.conv.activeContext.isInActionLoop = !!processedData.action.loop
this.conv.activeContext.actionName = processedData.action.next_action
this.conv.activeContext.intent = `${processedData.classification.skill}.${processedData.action.next_action}`

View File

@ -18,6 +18,10 @@
"name": "number"
}
},
"suggestions": [
"Yes",
"No thanks"
],
"next_action": "replay"
},
"replay": {

View File

@ -13,6 +13,11 @@
"1, 2, 3, FIRE!",
"Rock, paper, scissors..."
],
"suggestions": [
"Rock ✊",
"Paper ✋",
"Scissors ✌"
],
"next_action": "play"
},
"play": {
@ -23,6 +28,10 @@
"name": "handsign"
}
},
"suggestions": [
"Yes",
"No thanks"
],
"next_action": "rematch"
},
"rematch": {