mirror of
https://github.com/leon-ai/leon.git
synced 2024-11-30 10:44:25 +03:00
Merge branch 'feat/widgets' into develop
This commit is contained in:
commit
534853d67a
@ -1,7 +1,7 @@
|
||||
import { createElement } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import axios from 'axios'
|
||||
import { WidgetWrapper, Flexbox, Loader } from '@leon-ai/aurora'
|
||||
import { WidgetWrapper, Flexbox, Loader, Text } from '@leon-ai/aurora'
|
||||
|
||||
import renderAuroraComponent from './render-aurora-component'
|
||||
|
||||
@ -130,19 +130,33 @@ export default class Chatbot {
|
||||
const data = await axios.get(
|
||||
`${this.serverURL}/api/v1/fetch-widget?skill_action=${widgetContainer.onFetchAction}&widget_id=${widgetContainer.widgetId}`
|
||||
)
|
||||
console.log('data', data.data)
|
||||
const fetchedWidget = data.data.widget
|
||||
const reactNode = renderAuroraComponent(
|
||||
this.socket,
|
||||
fetchedWidget.componentTree,
|
||||
fetchedWidget.supportedEvents
|
||||
)
|
||||
const reactNode = fetchedWidget
|
||||
? renderAuroraComponent(
|
||||
this.socket,
|
||||
fetchedWidget.componentTree,
|
||||
fetchedWidget.supportedEvents
|
||||
)
|
||||
: createElement(WidgetWrapper, {
|
||||
children: createElement(Flexbox, {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
children: createElement(Text, {
|
||||
secondary: true,
|
||||
children: 'This widget has been deleted.'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
widgetContainer.reactRootNode.render(reactNode)
|
||||
WIDGETS_FETCH_CACHE.set(widgetContainer.widgetId, {
|
||||
...fetchedWidget,
|
||||
reactNode
|
||||
})
|
||||
this.scrollDown()
|
||||
setTimeout(() => {
|
||||
this.scrollDown()
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -44,18 +44,26 @@ class Leon {
|
||||
|
||||
if (data != null) {
|
||||
for (const key in data) {
|
||||
// In case the answer needs speech and text differentiation
|
||||
if (typeof answer !== 'string' && answer.text) {
|
||||
answer.text = answer.text.replaceAll(`%${key}%`, String(data[key]))
|
||||
answer.speech = answer.speech.replaceAll(
|
||||
`%${key}%`,
|
||||
String(data[key])
|
||||
)
|
||||
} else {
|
||||
if (typeof answer === 'string') {
|
||||
answer = (answer as string).replaceAll(
|
||||
`%${key}%`,
|
||||
String(data[key])
|
||||
)
|
||||
} else {
|
||||
// In case the answer needs speech and text differentiation
|
||||
|
||||
if (answer.text) {
|
||||
answer.text = answer.text.replaceAll(
|
||||
`%${key}%`,
|
||||
String(data[key])
|
||||
)
|
||||
}
|
||||
if (answer.speech) {
|
||||
answer.speech = answer.speech.replaceAll(
|
||||
`%${key}%`,
|
||||
String(data[key])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,21 +72,26 @@ class Leon {
|
||||
const { variables } = SKILL_CONFIG
|
||||
|
||||
for (const key in variables) {
|
||||
// In case the answer needs speech and text differentiation
|
||||
if (typeof answer !== 'string' && answer.text) {
|
||||
answer.text = answer.text.replaceAll(
|
||||
`%${key}%`,
|
||||
String(variables[key])
|
||||
)
|
||||
answer.speech = answer.speech.replaceAll(
|
||||
`%${key}%`,
|
||||
String(variables[key])
|
||||
)
|
||||
} else {
|
||||
if (typeof answer === 'string') {
|
||||
answer = (answer as string).replaceAll(
|
||||
`%${key}%`,
|
||||
String(variables[key])
|
||||
)
|
||||
} else {
|
||||
// In case the answer needs speech and text differentiation
|
||||
|
||||
if (answer.text) {
|
||||
answer.text = answer.text.replaceAll(
|
||||
`%${key}%`,
|
||||
String(variables[key])
|
||||
)
|
||||
}
|
||||
if (answer.speech) {
|
||||
answer.speech = answer.speech.replaceAll(
|
||||
`%${key}%`,
|
||||
String(variables[key])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,5 @@
|
||||
{
|
||||
"endpoints": [
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/unknown/widget-playground/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/games/akinator/choose_thematic",
|
||||
@ -57,17 +52,6 @@
|
||||
"route": "/api/action/games/rochambeau/rematch",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/news/github_trends/run",
|
||||
"params": ["number", "daterange"],
|
||||
"entitiesType": "builtIn"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/news/product_hunt_trends/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/leon/age/run",
|
||||
@ -143,6 +127,17 @@
|
||||
"route": "/api/action/leon/thanks/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/news/github_trends/run",
|
||||
"params": ["number", "daterange"],
|
||||
"entitiesType": "builtIn"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/news/product_hunt_trends/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/create_list",
|
||||
@ -190,6 +185,36 @@
|
||||
"params": ["todos", "list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/conversation/setup",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/conversation/chit_chat",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/conversation/converse",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/mbti/setup",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/mbti/quiz",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/unknown/widget-playground/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/utilities/date_time/current_date_time",
|
||||
@ -246,16 +271,6 @@
|
||||
"route": "/api/action/utilities/timer/cancel_timer",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/utilities/timer/pause_timer",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/utilities/timer/resume_timer",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/utilities/timer/check_timer",
|
||||
@ -275,31 +290,6 @@
|
||||
"method": "GET",
|
||||
"route": "/api/action/utilities/translator-poc/translate",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/conversation/setup",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/conversation/chit_chat",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/conversation/converse",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/mbti/setup",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/mbti/quiz",
|
||||
"params": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ export default class Brain {
|
||||
}, naturalStartTypingDelay)
|
||||
// Next answer to handle
|
||||
const answer = this.answerQueue.pop()
|
||||
let textAnswer = ''
|
||||
let textAnswer: string | undefined = ''
|
||||
let speechAnswer = ''
|
||||
|
||||
if (answer && answer !== '') {
|
||||
@ -208,21 +208,33 @@ export default class Brain {
|
||||
!hasLoopConfig &&
|
||||
!hasSlotsConfig
|
||||
) {
|
||||
if (speechAnswer === textAnswer || typeof answer === 'string') {
|
||||
if (
|
||||
speechAnswer === textAnswer ||
|
||||
typeof answer === 'string' ||
|
||||
answer.speech
|
||||
) {
|
||||
/**
|
||||
* Only use LLM NLG if the answer is not too short
|
||||
* otherwise it will be too hard for the model to generate a meaningful text
|
||||
*/
|
||||
const nbOfWords = String(answer).split(' ').length
|
||||
const textToParaphrase = textAnswer ?? speechAnswer
|
||||
const nbOfWords = String(textToParaphrase).split(' ').length
|
||||
if (nbOfWords >= MIN_NB_OF_WORDS_TO_USE_LLM_NLG) {
|
||||
const paraphraseDuty = new ParaphraseLLMDuty({
|
||||
input: textAnswer
|
||||
input: textToParaphrase
|
||||
})
|
||||
await paraphraseDuty.init()
|
||||
const paraphraseResult = await paraphraseDuty.execute()
|
||||
const paraphraseResult = await paraphraseDuty.execute({
|
||||
// Do not generate tokens when only a speech answer is needed
|
||||
shouldEmitOnToken: !!(!textAnswer && speechAnswer)
|
||||
})
|
||||
|
||||
textAnswer = paraphraseResult?.output as unknown as string
|
||||
speechAnswer = textAnswer
|
||||
if (!textAnswer) {
|
||||
speechAnswer = paraphraseResult?.output as unknown as string
|
||||
} else {
|
||||
textAnswer = paraphraseResult?.output as unknown as string
|
||||
speechAnswer = textAnswer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,17 +261,24 @@ export default class Brain {
|
||||
})
|
||||
})*/
|
||||
|
||||
SOCKET_SERVER.socket?.emit('answer', textAnswer)
|
||||
SOCKET_SERVER.socket?.emit('is-typing', false)
|
||||
/**
|
||||
* Only send an answer when the text answer is defined.
|
||||
* It may happen that only a speech is needed
|
||||
*/
|
||||
if (textAnswer) {
|
||||
SOCKET_SERVER.socket?.emit('answer', textAnswer)
|
||||
|
||||
await CONVERSATION_LOGGER.push({
|
||||
who: 'owner',
|
||||
message: NLU.nluResult.newUtterance
|
||||
})
|
||||
await CONVERSATION_LOGGER.push({
|
||||
who: 'leon',
|
||||
message: textAnswer
|
||||
})
|
||||
await CONVERSATION_LOGGER.push({
|
||||
who: 'owner',
|
||||
message: NLU.nluResult.newUtterance
|
||||
})
|
||||
await CONVERSATION_LOGGER.push({
|
||||
who: 'leon',
|
||||
message: textAnswer
|
||||
})
|
||||
}
|
||||
|
||||
SOCKET_SERVER.socket?.emit('is-typing', false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,7 +467,7 @@ export default class Brain {
|
||||
|
||||
const { answer } = skillAnswer.output
|
||||
if (!this.isMuted) {
|
||||
this.talk(answer)
|
||||
this.talk(answer, true)
|
||||
}
|
||||
this.skillOutput = data.toString()
|
||||
|
||||
|
@ -138,7 +138,7 @@ The sun is a star, it is the closest star to Earth.`
|
||||
session: ParaphraseLLMDuty.session,
|
||||
maxTokens: LLM_MANAGER.context.contextSize,
|
||||
onToken: (chunk) => {
|
||||
if (!params.isWarmingUp) {
|
||||
if (!params.isWarmingUp && !params.shouldEmitOnToken) {
|
||||
const detokenizedChunk = LLM_PROVIDER.cleanUpResult(
|
||||
LLM_MANAGER.model.detokenize(chunk)
|
||||
)
|
||||
|
@ -27,6 +27,7 @@ export interface LLMDutyInitParams {
|
||||
}
|
||||
export interface LLMDutyExecuteParams {
|
||||
isWarmingUp?: boolean
|
||||
shouldEmitOnToken?: boolean
|
||||
}
|
||||
export interface LLMDutyParams {
|
||||
input: string | null
|
||||
@ -46,7 +47,8 @@ export const DEFAULT_INIT_PARAMS: LLMDutyInitParams = {
|
||||
force: false
|
||||
}
|
||||
export const DEFAULT_EXECUTE_PARAMS: LLMDutyExecuteParams = {
|
||||
isWarmingUp: false
|
||||
isWarmingUp: false,
|
||||
shouldEmitOnToken: true
|
||||
}
|
||||
|
||||
export abstract class LLMDuty {
|
||||
|
@ -28,7 +28,7 @@ const answerTypes = Type.Union([
|
||||
Type.String(),
|
||||
Type.Object({
|
||||
speech: Type.String(),
|
||||
text: Type.String()
|
||||
text: Type.Optional(Type.String())
|
||||
})
|
||||
])
|
||||
const skillCustomEnumEntityType = Type.Object(
|
||||
|
@ -19,14 +19,6 @@
|
||||
"Don't need the timer"
|
||||
]
|
||||
},
|
||||
"pause_timer": {
|
||||
"type": "logic",
|
||||
"utterance_samples": ["Pause the timer", "Put the timer on hold"]
|
||||
},
|
||||
"resume_timer": {
|
||||
"type": "logic",
|
||||
"utterance_samples": ["[Resume|Continue] the timer"]
|
||||
},
|
||||
"check_timer": {
|
||||
"type": "logic",
|
||||
"utterance_samples": [
|
||||
@ -37,6 +29,11 @@
|
||||
}
|
||||
},
|
||||
"answers": {
|
||||
"timer_set": [
|
||||
{
|
||||
"speech": "Done. I will let you know when time is up."
|
||||
}
|
||||
],
|
||||
"cannot_get_duration": [
|
||||
"You should provide a duration for the timer.",
|
||||
"You didn't provide a duration for the timer."
|
||||
@ -45,7 +42,8 @@
|
||||
"Sorry, I can't set a timer for this unit. Use %hours%, %minutes% or %seconds% instead.",
|
||||
"I can't set a timer for this duration. Use %hours%, %minutes% or %seconds% instead."
|
||||
],
|
||||
"no_timer_set": ["No timer is set.", "There is no timer set."]
|
||||
"no_timer_set": ["No timer is set.", "There is no timer set."],
|
||||
"timer_canceled": ["The timer is canceled.", "Timer is stopped."]
|
||||
},
|
||||
"widget_contents": {
|
||||
"second_unit": "second",
|
||||
|
10
skills/utilities/timer/src/actions/cancel_timer.ts
Normal file
10
skills/utilities/timer/src/actions/cancel_timer.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import type { ActionFunction } from '@sdk/types'
|
||||
import { leon } from '@sdk/leon'
|
||||
|
||||
import { deleteAllTimersMemory } from '../lib/memory'
|
||||
|
||||
export const run: ActionFunction = async function () {
|
||||
await deleteAllTimersMemory()
|
||||
|
||||
await leon.answer({ key: 'timer_canceled' })
|
||||
}
|
@ -16,13 +16,16 @@ export const run: ActionFunction = async function () {
|
||||
}
|
||||
|
||||
const { interval, finishedAt, duration } = timerMemory
|
||||
const remainingTime = finishedAt - Math.floor(Date.now() / 1_000)
|
||||
let remainingTime = finishedAt - Math.floor(Date.now() / 1_000)
|
||||
if (remainingTime <= 0) {
|
||||
remainingTime = 0
|
||||
}
|
||||
const initialProgress = 100 - (remainingTime / duration) * 100
|
||||
|
||||
const timerWidget = new TimerWidget({
|
||||
params: {
|
||||
id: widgetId ?? timerMemory.widgetId,
|
||||
seconds: remainingTime <= 0 ? 0 : remainingTime,
|
||||
seconds: remainingTime,
|
||||
initialProgress,
|
||||
initialDuration: duration,
|
||||
interval
|
||||
|
@ -39,5 +39,8 @@ export const run: ActionFunction = async function (params) {
|
||||
widget: timerWidget,
|
||||
speech: 'I set a timer for ... ...'
|
||||
})*/
|
||||
await leon.answer({ widget: timerWidget })
|
||||
await leon.answer({
|
||||
widget: timerWidget,
|
||||
key: 'timer_set'
|
||||
})
|
||||
}
|
||||
|
@ -49,3 +49,7 @@ export async function getNewestTimerMemory(): Promise<TimerMemory | null> {
|
||||
|
||||
return timersMemory[timersMemory.length - 1] || null
|
||||
}
|
||||
|
||||
export function deleteAllTimersMemory(): Promise<TimerMemory[]> {
|
||||
return TIMERS_MEMORY.write([])
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user