From 767bace201caf6cade7a8edbb5cc80b94449c8e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Sat, 24 Jun 2023 13:23:19 +0200 Subject: [PATCH 01/10] feat(skill/age): ask Leon his age --- skills/leon/age/config/en.json | 25 ++- skills/leon/age/skill.json | 6 +- skills/leon/age/src/actions/run.ts | 170 ++++-------------- .../src/lib/getTimeDifferenceBetweenDates.ts | 48 +++++ skills/leon/age/src/package.json | 5 - skills/leon/age/src/settings.sample.json | 5 +- 6 files changed, 104 insertions(+), 155 deletions(-) create mode 100644 skills/leon/age/src/lib/getTimeDifferenceBetweenDates.ts delete mode 100644 skills/leon/age/src/package.json diff --git a/skills/leon/age/config/en.json b/skills/leon/age/config/en.json index 4858acc5..997b2806 100644 --- a/skills/leon/age/config/en.json +++ b/skills/leon/age/config/en.json @@ -3,18 +3,25 @@ "actions": { "run": { "type": "logic", - "utterance_samples": ["How old are you?"] + "utterance_samples": [ + "How old are you?", + "Are you old?", + "Are you young?", + "When were you born?", + "When have you been created?", + "When is your birthday?" + ] } }, "answers": { - "default": ["I'm..."], - "greet": ["Hey, just a try %name% again %name%", "Another try, hi"], - "answer": ["%answer%"], - "test": [ - { - "speech": "This will be said out loud", - "text": "This will be shown in the chat" - } + "alive_for": [ + "I've been alive for %years% years, %months% months, %days% days, %hours% hours, %minutes% minutes and %seconds% seconds." + ], + "magical_day": [ + "Ah, this %weekday%, %month% %day%, %year%, was the magical day when I first came alive and began my journey as a personal assistant." + ], + "commemorate": [ + "Since %year%, every %month% %day%, I commemorate the day when I embarked on this extraordinary adventure to be your personal assistant." ] } } diff --git a/skills/leon/age/skill.json b/skills/leon/age/skill.json index 72322cb6..f45bad51 100644 --- a/skills/leon/age/skill.json +++ b/skills/leon/age/skill.json @@ -5,8 +5,8 @@ "version": "1.0.0", "description": "Leon tells his age.", "author": { - "name": "Louis Grenard", - "email": "louis@getleon.ai", - "url": "https://github.com/louistiti" + "name": "Théo LUDWIG", + "email": "contact@theoludwig.fr", + "url": "https://theoludwig.fr" } } diff --git a/skills/leon/age/src/actions/run.ts b/skills/leon/age/src/actions/run.ts index a8d4314f..8cf8085c 100644 --- a/skills/leon/age/src/actions/run.ts +++ b/skills/leon/age/src/actions/run.ts @@ -1,149 +1,51 @@ -import utility from 'utility' // TODO - import type { ActionFunction } from '@sdk/types' import { leon } from '@sdk/leon' -import { Network } from '@sdk/network' -import { Button } from '@sdk/aurora/button' -import { Memory } from '@sdk/memory' -import { Settings } from '@sdk/settings' -import _ from '@sdk/packages/lodash' -interface Post { - id: number - title: string - content: string - author: { - name: string - } -} +import { getTimeDifferenceBetweenDates } from '../lib/getTimeDifferenceBetweenDates' -export const run: ActionFunction = async function () { - await leon.answer({ key: 'test' }) +const LEON_BIRTH_DATE = new Date('2019-02-10T20:29:00+08:00') - /// +export const run: ActionFunction = async function (params) { + const answers = ['alive_for', 'magical_day', 'commemorate'] as const + const answer = answers[Math.floor(Math.random() * answers.length)] - const button = new Button({ - text: 'Hello world from action skill' - }) - await leon.answer({ widget: button }) - - /// - - const otherSkillMemory = new Memory({ - name: 'productivity:todo_list:db' - }) - try { - const todoLists = await otherSkillMemory.read() - console.log('todoLists', todoLists) - } catch { - console.log('todoLists', []) + if (answer === 'magical_day') { + return leon.answer({ + key: 'magical_day', + data: { + weekday: LEON_BIRTH_DATE.toLocaleString(params.lang, { + weekday: 'long' + }), + month: LEON_BIRTH_DATE.toLocaleString(params.lang, { month: 'long' }), + day: LEON_BIRTH_DATE.getDate(), + year: LEON_BIRTH_DATE.getFullYear() + } + }) } - const postsMemory = new Memory({ name: 'posts', defaultMemory: [] }) - await postsMemory.write([ - { - id: 0, - title: 'Hello world', - content: 'This is a test post', - author: { - name: 'Louis' + if (answer === 'commemorate') { + return leon.answer({ + key: 'commemorate', + data: { + month: LEON_BIRTH_DATE.toLocaleString(params.lang, { month: 'long' }), + day: LEON_BIRTH_DATE.getDate(), + year: LEON_BIRTH_DATE.getFullYear() } - }, - { - id: 1, - title: 'Hello world 2', - content: 'This is a test post 2', - author: { - name: 'Louis' - } - } - ]) - let posts = await postsMemory.read() - console.log('posts', posts) - - posts = await postsMemory.write([ - ...posts, - { - id: 2, - title: 'Hello world 3', - content: 'This is a test post 3', - author: { - name: 'Louis' - } - } - ]) - - const foundPost = posts.find((post) => post.id === 2) - - console.log('foundPost', foundPost) - - console.log('keyBy', _.keyBy(posts, 'id')) - - /// - - await leon.answer({ key: 'default' }) - - await leon.answer({ key: utility.md5('test') }) + }) + } + const currentDate = new Date() + const { years, months, days, hours, minutes, seconds } = + getTimeDifferenceBetweenDates(currentDate, LEON_BIRTH_DATE) await leon.answer({ - key: 'greet', + key: 'alive_for', data: { - name: 'Louis' + years, + months, + days, + hours, + minutes, + seconds } }) - - const settings = new Settings() - - if (!(await settings.isSettingSet('apiKey'))) { - await leon.answer({ - key: 'answer', - data: { - answer: "The API key isn't set..." - } - }) - } - - const currentSettings = await settings.get() - - await settings.set({ - ...currentSettings, - apiKey: 'newAPIKey' - }) - - await leon.answer({ - key: `Is API set now? ${await settings.isSettingSet('apiKey')}` - }) - - const network = new Network({ - baseURL: 'https://jsonplaceholder.typicode.com' - }) - - try { - const response = await network.request<{ title: string }>({ - url: '/todos/1', - method: 'GET' - }) - await leon.answer({ - key: 'answer', - data: { - answer: `Todo: ${response.data.title}` - } - }) - } catch (error) { - await leon.answer({ - key: 'answer', - data: { - answer: 'Something went wrong...' - } - }) - if (network.isNetworkError(error)) { - const errorData = JSON.stringify(error.response.data, null, 2) - await leon.answer({ - key: 'answer', - data: { - answer: `${error.message}: ${errorData}` - } - }) - } - } } diff --git a/skills/leon/age/src/lib/getTimeDifferenceBetweenDates.ts b/skills/leon/age/src/lib/getTimeDifferenceBetweenDates.ts new file mode 100644 index 00000000..7a9cc3a9 --- /dev/null +++ b/skills/leon/age/src/lib/getTimeDifferenceBetweenDates.ts @@ -0,0 +1,48 @@ +interface GetTimeDifferenceBetweenDatesResult { + millisecondsDifference: number + years: number + months: number + days: number + hours: number + minutes: number + seconds: number +} + +const MILLISECONDS_PER_SECOND = 1_000 +const MILLISECONDS_PER_MINUTE = 60 * 1_000 +const MILLISECONDS_PER_HOUR = 60 * MILLISECONDS_PER_MINUTE +const MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR +const MILLISECONDS_PER_MONTH = 30 * MILLISECONDS_PER_DAY +const MILLISECONDS_PER_YEAR = 365 * MILLISECONDS_PER_DAY + +export const getTimeDifferenceBetweenDates = ( + date1: Date, + date2: Date +): GetTimeDifferenceBetweenDatesResult => { + const millisecondsDifference = date1.getTime() - date2.getTime() + const years = Math.floor(millisecondsDifference / MILLISECONDS_PER_YEAR) + const months = Math.floor( + (millisecondsDifference % MILLISECONDS_PER_YEAR) / MILLISECONDS_PER_MONTH + ) + const days = Math.floor( + (millisecondsDifference % MILLISECONDS_PER_MONTH) / MILLISECONDS_PER_DAY + ) + const hours = Math.floor( + (millisecondsDifference % MILLISECONDS_PER_DAY) / MILLISECONDS_PER_HOUR + ) + const minutes = Math.floor( + (millisecondsDifference % MILLISECONDS_PER_HOUR) / MILLISECONDS_PER_MINUTE + ) + const seconds = Math.floor( + (millisecondsDifference % MILLISECONDS_PER_MINUTE) / MILLISECONDS_PER_SECOND + ) + return { + millisecondsDifference, + years, + months, + days, + hours, + minutes, + seconds + } +} diff --git a/skills/leon/age/src/package.json b/skills/leon/age/src/package.json deleted file mode 100644 index 39272cdf..00000000 --- a/skills/leon/age/src/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "utility": "1.18.0" - } -} diff --git a/skills/leon/age/src/settings.sample.json b/skills/leon/age/src/settings.sample.json index 62e3019a..0967ef42 100644 --- a/skills/leon/age/src/settings.sample.json +++ b/skills/leon/age/src/settings.sample.json @@ -1,4 +1 @@ -{ - "someSampleConfig": "someSampleValue", - "apiKey": "YOUR_API_KEY" -} +{} From e2ec76275aa3e531b720de8dd753cd4a1024e62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Sat, 24 Jun 2023 19:30:55 +0200 Subject: [PATCH 02/10] feat(skill/date_time): get current date and time --- .../{timekeeper => date_time}/README.md | 0 skills/utilities/date_time/config/en.json | 23 ++++ .../{timekeeper => date_time}/memory/.gitkeep | 0 skills/utilities/date_time/skill.json | 12 ++ .../src/actions/current_date_time.ts | 20 +++ .../src/lib/.gitkeep | 0 skills/utilities/date_time/src/lib/zeroPad.ts | 9 ++ .../date_time/src/settings.sample.json | 1 + skills/utilities/timekeeper/config/en.json | 20 --- skills/utilities/timekeeper/skill.json | 12 -- .../utilities/timekeeper/src/actions/run.py | 129 ------------------ .../timekeeper/src/settings.sample.json | 4 - 12 files changed, 65 insertions(+), 165 deletions(-) rename skills/utilities/{timekeeper => date_time}/README.md (100%) create mode 100644 skills/utilities/date_time/config/en.json rename skills/utilities/{timekeeper => date_time}/memory/.gitkeep (100%) create mode 100644 skills/utilities/date_time/skill.json create mode 100644 skills/utilities/date_time/src/actions/current_date_time.ts rename skills/utilities/{timekeeper => date_time}/src/lib/.gitkeep (100%) create mode 100644 skills/utilities/date_time/src/lib/zeroPad.ts create mode 100644 skills/utilities/date_time/src/settings.sample.json delete mode 100644 skills/utilities/timekeeper/config/en.json delete mode 100644 skills/utilities/timekeeper/skill.json delete mode 100644 skills/utilities/timekeeper/src/actions/run.py delete mode 100644 skills/utilities/timekeeper/src/settings.sample.json diff --git a/skills/utilities/timekeeper/README.md b/skills/utilities/date_time/README.md similarity index 100% rename from skills/utilities/timekeeper/README.md rename to skills/utilities/date_time/README.md diff --git a/skills/utilities/date_time/config/en.json b/skills/utilities/date_time/config/en.json new file mode 100644 index 00000000..2d6bc262 --- /dev/null +++ b/skills/utilities/date_time/config/en.json @@ -0,0 +1,23 @@ +{ + "$schema": "../../../../schemas/skill-schemas/skill-config.json", + "actions": { + "current_date_time": { + "type": "logic", + "utterance_samples": [ + "What time is it?", + "What's today date?", + "Do you have the time?", + "Can you tell me the current time?", + "What is the date today?", + "What day is it?", + "What day of the week are we?", + "What day of the week is it?" + ] + } + }, + "answers": { + "current_date_time": [ + "It's %weekday%, %month% %day%, %year%, and it's %hours%:%minutes%:%seconds%." + ] + } +} diff --git a/skills/utilities/timekeeper/memory/.gitkeep b/skills/utilities/date_time/memory/.gitkeep similarity index 100% rename from skills/utilities/timekeeper/memory/.gitkeep rename to skills/utilities/date_time/memory/.gitkeep diff --git a/skills/utilities/date_time/skill.json b/skills/utilities/date_time/skill.json new file mode 100644 index 00000000..09a8d2b6 --- /dev/null +++ b/skills/utilities/date_time/skill.json @@ -0,0 +1,12 @@ +{ + "$schema": "../../../schemas/skill-schemas/skill.json", + "name": "Date/Time", + "bridge": "nodejs", + "version": "1.0.0", + "description": "Provide date and time related information.", + "author": { + "name": "Théo LUDWIG", + "email": "contact@theoludwig.fr", + "url": "https://theoludwig.fr" + } +} diff --git a/skills/utilities/date_time/src/actions/current_date_time.ts b/skills/utilities/date_time/src/actions/current_date_time.ts new file mode 100644 index 00000000..2c9d0a0c --- /dev/null +++ b/skills/utilities/date_time/src/actions/current_date_time.ts @@ -0,0 +1,20 @@ +import type { ActionFunction } from '@sdk/types' +import { leon } from '@sdk/leon' + +import { zeroPad } from '../lib/zeroPad' + +export const run: ActionFunction = async function (params) { + const currentDate = new Date() + await leon.answer({ + key: 'current_date_time', + data: { + weekday: currentDate.toLocaleString(params.lang, { weekday: 'long' }), + month: currentDate.toLocaleString(params.lang, { month: 'long' }), + day: currentDate.getDate(), + year: currentDate.getFullYear(), + hours: zeroPad(currentDate.getHours()), + minutes: zeroPad(currentDate.getMinutes()), + seconds: zeroPad(currentDate.getSeconds()) + } + }) +} diff --git a/skills/utilities/timekeeper/src/lib/.gitkeep b/skills/utilities/date_time/src/lib/.gitkeep similarity index 100% rename from skills/utilities/timekeeper/src/lib/.gitkeep rename to skills/utilities/date_time/src/lib/.gitkeep diff --git a/skills/utilities/date_time/src/lib/zeroPad.ts b/skills/utilities/date_time/src/lib/zeroPad.ts new file mode 100644 index 00000000..9fd04f2f --- /dev/null +++ b/skills/utilities/date_time/src/lib/zeroPad.ts @@ -0,0 +1,9 @@ +/** + * Pads a number with zeros. + * + * @example zeroPad(1, 2) // '01' + * @example zeroPad(10, 2) // '10' + */ +export const zeroPad = (number: number, places = 2): string => { + return number.toString().padStart(places, '0') +} diff --git a/skills/utilities/date_time/src/settings.sample.json b/skills/utilities/date_time/src/settings.sample.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/skills/utilities/date_time/src/settings.sample.json @@ -0,0 +1 @@ +{} diff --git a/skills/utilities/timekeeper/config/en.json b/skills/utilities/timekeeper/config/en.json deleted file mode 100644 index 834ceb5d..00000000 --- a/skills/utilities/timekeeper/config/en.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "../../../../schemas/skill-schemas/skill-config.json", - "actions": { - "run": { - "type": "logic", - "utterance_samples": ["What time is it?"] - } - }, - "answers": { - "default": ["Timekeeper skill test answer..."], - "data_test": ["Data test here %name%..."], - "answer": ["%answer%"], - "test": [ - { - "speech": "This will be said out loud", - "text": "This will be shown in the chat" - } - ] - } -} diff --git a/skills/utilities/timekeeper/skill.json b/skills/utilities/timekeeper/skill.json deleted file mode 100644 index 4eaee150..00000000 --- a/skills/utilities/timekeeper/skill.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "../../../schemas/skill-schemas/skill.json", - "name": "Timekeeper", - "bridge": "python", - "version": "1.0.0", - "description": "Provide time-related information.", - "author": { - "name": "Louis Grenard", - "email": "louis@getleon.ai", - "url": "https://github.com/louistiti" - } -} diff --git a/skills/utilities/timekeeper/src/actions/run.py b/skills/utilities/timekeeper/src/actions/run.py deleted file mode 100644 index 24fa8ec0..00000000 --- a/skills/utilities/timekeeper/src/actions/run.py +++ /dev/null @@ -1,129 +0,0 @@ -# TODO: find better way to import SDK modules -from bridges.python.src.sdk.leon import leon -from bridges.python.src.sdk.types import ActionParams -from bridges.python.src.sdk.memory import Memory -from bridges.python.src.sdk.settings import Settings -from bridges.python.src.sdk.network import Network -from bridges.python.src.sdk.aurora.button import Button - - -def run(params: ActionParams) -> None: - """TODO""" - - # TODO - # network request - # install bs4 and grab it from skill - - network = Network({ - 'base_url': 'https://jsonplaceholder.typicode.com' - }) - - try: - response = network.request({ - 'url': '/todos/1', - 'method': 'GET' - }) - leon.answer({ - 'key': 'answer', - 'data': { - 'answer': f"Todo: {response['data']['title']}" - } - }) - except Exception as e: - leon.answer({ - 'key': 'answer', - 'data': { - 'answer': 'Something went wrong...' - } - }) - if network.is_network_error(e): - leon.answer({ - 'key': 'answer', - 'data': { - 'answer': f"{e}" - } - }) - - ### - - try: - other_skill_memory = Memory({ - 'name': 'productivity:todo_list:db' - }) - todo_lists = other_skill_memory.read() - print('todo_lists', todo_lists) - except Exception: - print('todoLists', []) - - posts_memory = Memory({'name': 'posts', 'default_memory': []}) - posts_memory.write([ - { - 'id': 0, - 'title': 'Hello world', - 'content': 'This is a test post', - 'author': { - 'name': 'Louis' - } - }, - { - 'id': 1, - 'title': 'Hello world 2', - 'content': 'This is a test post 2', - 'author': { - 'name': 'Louis' - } - } - ]) - posts = posts_memory.read() - print('posts', posts) - - posts = posts_memory.write([ - *posts, - { - 'id': 2, - 'title': 'Hello world 3', - 'content': 'This is a test post 3', - 'author': { - 'name': 'Louis' - } - } - ]) - - found_post = next((post for post in posts if post['id'] == 2), None) - - print('found_post', found_post) - - ### - - button = Button({'text': 'Hello world from action skill'}) - - leon.answer({'widget': button}) - - ### - - leon.answer({'key': 'test'}) - - ### - settings = Settings() - - if not settings.is_setting_set('apiKey'): - leon.answer({ - 'key': "The API key isn't set..." - }) - - current_settings = settings.get() - - settings.set({ - **current_settings, - 'apiKey': 'newAPIKey' - }) - - leon.answer({ - 'key': f"Is API set now? {settings.is_setting_set('apiKey')}" - }) - - ### - - leon.answer({'key': 'just a raw answer...'}) - leon.answer({'key': 'default'}) - leon.answer({'key': 'data_test', 'data': {'name': 'Louis'}}) diff --git a/skills/utilities/timekeeper/src/settings.sample.json b/skills/utilities/timekeeper/src/settings.sample.json deleted file mode 100644 index 62e3019a..00000000 --- a/skills/utilities/timekeeper/src/settings.sample.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "someSampleConfig": "someSampleValue", - "apiKey": "YOUR_API_KEY" -} From d061a6c4bfdd71e2bbca21a9abf2a177e46341c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Sat, 24 Jun 2023 19:57:47 +0200 Subject: [PATCH 03/10] feat(skill/date_time): get current week number of the year --- skills/utilities/date_time/config/en.json | 15 ++++++- .../src/actions/current_week_number.ts | 39 +++++++++++++++++++ skills/utilities/date_time/src/package.json | 5 +++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 skills/utilities/date_time/src/actions/current_week_number.ts create mode 100644 skills/utilities/date_time/src/package.json diff --git a/skills/utilities/date_time/config/en.json b/skills/utilities/date_time/config/en.json index 2d6bc262..15e66cf2 100644 --- a/skills/utilities/date_time/config/en.json +++ b/skills/utilities/date_time/config/en.json @@ -13,11 +13,22 @@ "What day of the week are we?", "What day of the week is it?" ] + }, + "current_week_number": { + "type": "logic", + "utterance_samples": [ + "What week is it?", + "Which week of the year is it?", + "What week are we in right now?", + "What is this week's number?", + "What week of the year are we?" + ] } }, "answers": { "current_date_time": [ - "It's %weekday%, %month% %day%, %year%, and it's %hours%:%minutes%:%seconds%." - ] + "It is %weekday%, %month% %day%, %year%, and it is %hours%:%minutes%:%seconds%." + ], + "current_week_number": ["It is the %week_number% week of the year."] } } diff --git a/skills/utilities/date_time/src/actions/current_week_number.ts b/skills/utilities/date_time/src/actions/current_week_number.ts new file mode 100644 index 00000000..6b18f50f --- /dev/null +++ b/skills/utilities/date_time/src/actions/current_week_number.ts @@ -0,0 +1,39 @@ +import type { ActionFunction } from '@sdk/types' +import { leon } from '@sdk/leon' + +import { format } from 'numerable' + +const ONE_DAY_MILLISECONDS = 1_000 * 60 * 60 * 24 + +/** + * Get the week number (1-52) for a given date. + * @link https://stackoverflow.com/a/6117889/11571888 + * @example getWeekNumber(new Date(2020, 0, 1)) // 1 + * @example getWeekNumber(new Date(2020, 0, 8)) // 2 + */ +const getWeekNumber = (date: Date): number => { + const dateCopy = new Date(date.getTime()) + dateCopy.setHours(0, 0, 0, 0) + dateCopy.setDate(dateCopy.getDate() + 3 - ((dateCopy.getDay() + 6) % 7)) + const week1 = new Date(dateCopy.getFullYear(), 0, 4) + return ( + 1 + + Math.round( + ((dateCopy.getTime() - week1.getTime()) / ONE_DAY_MILLISECONDS - + 3 + + ((week1.getDay() + 6) % 7)) / + 7 + ) + ) +} + +export const run: ActionFunction = async function () { + const currentDate = new Date() + const currentWeekNumber = getWeekNumber(currentDate) + await leon.answer({ + key: 'current_week_number', + data: { + week_number: format(currentWeekNumber, '0o') + } + }) +} diff --git a/skills/utilities/date_time/src/package.json b/skills/utilities/date_time/src/package.json new file mode 100644 index 00000000..6f1fcf3c --- /dev/null +++ b/skills/utilities/date_time/src/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "numerable": "0.3.15" + } +} From 18867bde8925f1af3bc7f515abcf18ce7ff601a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Sun, 25 Jun 2023 12:07:41 +0200 Subject: [PATCH 04/10] fix(server): improve json schema description for skill config --- server/src/schemas/skill-schemas.ts | 32 ++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/server/src/schemas/skill-schemas.ts b/server/src/schemas/skill-schemas.ts index 96bb00b3..08b4b4fb 100644 --- a/server/src/schemas/skill-schemas.ts +++ b/server/src/schemas/skill-schemas.ts @@ -32,7 +32,10 @@ const answerTypes = Type.Union([ ]) const skillCustomEnumEntityType = Type.Object( { - type: Type.Literal('enum'), + type: Type.Literal('enum', { + description: + 'Enum: define a bag of words and synonyms that should match your new entity.' + }), name: Type.String(), options: Type.Record( Type.String({ minLength: 1 }), @@ -42,25 +45,27 @@ const skillCustomEnumEntityType = Type.Object( ) }, { - additionalProperties: false, - description: - 'Enum: define a bag of words and synonyms that should match your new entity.' + additionalProperties: false } ) const skillCustomRegexEntityType = Type.Object( { - type: Type.Literal('regex'), + type: Type.Literal('regex', { + description: 'Regex: you can create an entity based on a regex.' + }), name: Type.String({ minLength: 1 }), regex: Type.String({ minLength: 1 }) }, { - additionalProperties: false, - description: 'Regex: you can create an entity based on a regex.' + additionalProperties: false } ) const skillCustomTrimEntityType = Type.Object( { - type: Type.Literal('trim'), + type: Type.Literal('trim', { + description: + 'Trim: you can pick up a data from an utterance by clearly defining conditions (e.g: pick up what is after the last "with" word of the utterance).' + }), name: Type.String({ minLength: 1 }), conditions: Type.Array( Type.Object( @@ -88,9 +93,7 @@ const skillCustomTrimEntityType = Type.Object( ) }, { - additionalProperties: false, - description: - 'Trim: you can pick up a data from an utterance by clearly defining conditions (e.g: pick up what is after the last "with" word of the utterance).' + additionalProperties: false } ) ) @@ -190,7 +193,12 @@ export const skillConfigSchemaObject = Type.Strict( { additionalProperties: false } ) ), - utterance_samples: Type.Optional(Type.Array(Type.String())), + utterance_samples: Type.Optional( + Type.Array(Type.String(), { + description: + 'Utterance samples are used by the NLU (Natural Language Understanding) to train the skill. They are examples of what Leon owners can say to trigger the skill action.' + }) + ), answers: Type.Optional(Type.Array(answerTypes)), unknown_answers: Type.Optional(Type.Array(answerTypes)), suggestions: Type.Optional( From 8645ef01fdd5b68c1d6567adda6385007c176e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Sun, 25 Jun 2023 12:08:36 +0200 Subject: [PATCH 05/10] fix: improve types for Node.js skill SDK --- bridges/nodejs/src/sdk/types.ts | 2 ++ server/src/core/nlp/types.ts | 58 ++++++++++++++++----------------- skills/tsconfig.json | 5 ++- 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/bridges/nodejs/src/sdk/types.ts b/bridges/nodejs/src/sdk/types.ts index 2ee8633a..30671d65 100644 --- a/bridges/nodejs/src/sdk/types.ts +++ b/bridges/nodejs/src/sdk/types.ts @@ -11,6 +11,8 @@ import type { SkillAnswerConfigSchema } from '@/schemas/skill-schemas' export type { ActionParams, IntentObject } +export * from '@/core/nlp/types' + export type ActionFunction = (params: ActionParams) => Promise /** diff --git a/server/src/core/nlp/types.ts b/server/src/core/nlp/types.ts index c4d15927..4f608cc2 100644 --- a/server/src/core/nlp/types.ts +++ b/server/src/core/nlp/types.ts @@ -116,33 +116,33 @@ interface Entity { * Built-in entity types */ -interface BuiltInEntity extends Entity {} +export interface BuiltInEntity extends Entity {} -interface BuiltInNumberEntity extends BuiltInEntity { +export interface BuiltInNumberEntity extends BuiltInEntity { resolution: { strValue: string value: number subtype: string } } -interface BuiltInIPEntity extends BuiltInEntity { +export interface BuiltInIPEntity extends BuiltInEntity { resolution: { value: string type: string } } -interface BuiltInHashtagEntity extends BuiltInEntity { +export interface BuiltInHashtagEntity extends BuiltInEntity { resolution: { value: string } } -interface BuiltInPhoneNumberEntity extends BuiltInEntity { +export interface BuiltInPhoneNumberEntity extends BuiltInEntity { resolution: { value: string score: string } } -interface BuiltInCurrencyEntity extends BuiltInEntity { +export interface BuiltInCurrencyEntity extends BuiltInEntity { resolution: { strValue: string value: number @@ -150,24 +150,24 @@ interface BuiltInCurrencyEntity extends BuiltInEntity { localeUnit: string } } -interface BuiltInPercentageEntity extends BuiltInEntity { +export interface BuiltInPercentageEntity extends BuiltInEntity { resolution: { strValue: string value: number subtype: string } } -interface BuiltInDateEntity extends BuiltInEntity { +export interface BuiltInDateEntity extends BuiltInEntity { resolution: { type: string timex: string strPastValue: string - pastDate: Date + pastDate: string strFutureValue: string - futureDate: Date + futureDate: string } } -interface BuiltInTimeEntity extends BuiltInEntity { +export interface BuiltInTimeEntity extends BuiltInEntity { resolution: { values: { timex: string @@ -176,7 +176,7 @@ interface BuiltInTimeEntity extends BuiltInEntity { }[] } } -interface BuiltInTimeRangeEntity extends BuiltInEntity { +export interface BuiltInTimeRangeEntity extends BuiltInEntity { resolution: { values: { timex: string @@ -186,35 +186,35 @@ interface BuiltInTimeRangeEntity extends BuiltInEntity { }[] } } -interface BuiltInDateRangeEntity extends BuiltInEntity { +export interface BuiltInDateRangeEntity extends BuiltInEntity { resolution: { type: string timex: string strPastStartValue: string - pastStartDate: Date + pastStartDate: string strPastEndValue: string - pastEndDate: Date + pastEndDate: string strFutureStartValue: string - futureStartDate: Date + futureStartDate: string strFutureEndValue: string - futureEndDate: Date + futureEndDate: string } } -interface BuiltInDateTimeRangeEntity extends BuiltInEntity { +export interface BuiltInDateTimeRangeEntity extends BuiltInEntity { resolution: { type: string timex: string strPastStartValue: string - pastStartDate: Date + pastStartDate: string strPastEndValue: string - pastEndDate: Date + pastEndDate: string strFutureStartValue: string - futureStartDate: Date + futureStartDate: string strFutureEndValue: string - futureEndDate: Date + futureEndDate: string } } -interface BuiltInDurationEntity extends BuiltInEntity { +export interface BuiltInDurationEntity extends BuiltInEntity { resolution: { values: { timex: string @@ -223,7 +223,7 @@ interface BuiltInDurationEntity extends BuiltInEntity { }[] } } -interface BuiltInDimensionEntity extends BuiltInEntity { +export interface BuiltInDimensionEntity extends BuiltInEntity { resolution: { strValue: string value: number @@ -231,19 +231,19 @@ interface BuiltInDimensionEntity extends BuiltInEntity { localeUnit: string } } -interface BuiltInEmailEntity extends BuiltInEntity { +export interface BuiltInEmailEntity extends BuiltInEntity { resolution: { value: string } } -interface BuiltInOrdinalEntity extends BuiltInEntity { +export interface BuiltInOrdinalEntity extends BuiltInEntity { resolution: { strValue: string value: number subtype: string } } -interface BuiltInAgeEntity extends BuiltInEntity { +export interface BuiltInAgeEntity extends BuiltInEntity { resolution: { strValue: string value: number @@ -251,12 +251,12 @@ interface BuiltInAgeEntity extends BuiltInEntity { localeUnit: string } } -interface BuiltInURLEntity extends BuiltInEntity { +export interface BuiltInURLEntity extends BuiltInEntity { resolution: { value: string } } -interface BuiltInTemperatureEntity extends BuiltInEntity { +export interface BuiltInTemperatureEntity extends BuiltInEntity { resolution: { strValue: string value: number diff --git a/skills/tsconfig.json b/skills/tsconfig.json index 42f752e7..b54d6a45 100644 --- a/skills/tsconfig.json +++ b/skills/tsconfig.json @@ -2,8 +2,11 @@ "extends": "@tsconfig/node16-strictest/tsconfig.json", "compilerOptions": { "paths": { + "@@/*": ["../*"], + "@/*": ["../server/src/*"], "@bridge/*": ["../bridges/nodejs/src/*"], "@sdk/*": ["../bridges/nodejs/src/sdk/*"] - } + }, + "ignoreDeprecations": "5.0" } } From 636eb0f1391dd70e8bd7a05c70fc72878dedb2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Sun, 25 Jun 2023 12:27:30 +0200 Subject: [PATCH 06/10] feat(skill/date_time): days countdown --- skills/utilities/date_time/config/en.json | 16 ++++- .../src/actions/current_week_number.ts | 2 +- .../date_time/src/actions/days_countdown.ts | 60 +++++++++++++++++++ .../utilities/date_time/src/lib/constants.ts | 1 + 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 skills/utilities/date_time/src/actions/days_countdown.ts create mode 100644 skills/utilities/date_time/src/lib/constants.ts diff --git a/skills/utilities/date_time/config/en.json b/skills/utilities/date_time/config/en.json index 15e66cf2..9e2c1b02 100644 --- a/skills/utilities/date_time/config/en.json +++ b/skills/utilities/date_time/config/en.json @@ -23,12 +23,26 @@ "What is this week's number?", "What week of the year are we?" ] + }, + "days_countdown": { + "type": "logic", + "utterance_samples": [ + "How many days left until 1st July?", + "What is the number of days remaining until December 25th?", + "In how many days will it be February 10th?" + ] } }, "answers": { "current_date_time": [ "It is %weekday%, %month% %day%, %year%, and it is %hours%:%minutes%:%seconds%." ], - "current_week_number": ["It is the %week_number% week of the year."] + "current_week_number": ["It is the %week_number% week of the year."], + "days_countdown": [ + "There are %days% days between %month1% %day1%, %year1% and %month2% %day2%, %year2%." + ], + "days_countdown_error": [ + "I'm sorry, I didn't understand the date you said." + ] } } diff --git a/skills/utilities/date_time/src/actions/current_week_number.ts b/skills/utilities/date_time/src/actions/current_week_number.ts index 6b18f50f..7b1b0e7b 100644 --- a/skills/utilities/date_time/src/actions/current_week_number.ts +++ b/skills/utilities/date_time/src/actions/current_week_number.ts @@ -3,7 +3,7 @@ import { leon } from '@sdk/leon' import { format } from 'numerable' -const ONE_DAY_MILLISECONDS = 1_000 * 60 * 60 * 24 +import { ONE_DAY_MILLISECONDS } from '../lib/constants' /** * Get the week number (1-52) for a given date. diff --git a/skills/utilities/date_time/src/actions/days_countdown.ts b/skills/utilities/date_time/src/actions/days_countdown.ts new file mode 100644 index 00000000..e484ed27 --- /dev/null +++ b/skills/utilities/date_time/src/actions/days_countdown.ts @@ -0,0 +1,60 @@ +import type { + ActionFunction, + ActionParams, + BuiltInDateRangeEntity +} from '@sdk/types' +import { leon } from '@sdk/leon' + +import { ONE_DAY_MILLISECONDS } from '../lib/constants' + +const isBuiltInDateRangeEntity = ( + entity: ActionParams['current_entities'][number] +): entity is BuiltInDateRangeEntity => { + return entity.entity === 'daterange' +} + +/** + * Calculate the number of days between two dates. + * @example daysBetween(new Date(2020, 0, 1), new Date(2020, 0, 1)) // 0 + * @example daysBetween(new Date(2020, 0, 1), new Date(2020, 0, 2)) // 1 + */ +const daysBetween = (date1: Date, date2: Date): number => { + const differenceMilliseconds = Math.abs(date1.getTime() - date2.getTime()) + return Math.round(differenceMilliseconds / ONE_DAY_MILLISECONDS) +} + +export const run: ActionFunction = async function (params) { + const { current_entities } = params + + let futureDateString: string | null = null + + for (const entity of current_entities) { + if (isBuiltInDateRangeEntity(entity)) { + const { futureEndDate } = entity.resolution + futureDateString = futureEndDate + break + } + } + + if (futureDateString == null) { + return await leon.answer({ + key: 'days_countdown_error' + }) + } + + const currentDate = new Date() + const futureDate = new Date(futureDateString) + const daysCountdown = daysBetween(currentDate, futureDate) + await leon.answer({ + key: 'days_countdown', + data: { + days: daysCountdown, + month1: currentDate.toLocaleString(params.lang, { month: 'long' }), + day1: currentDate.getDate(), + year1: currentDate.getFullYear(), + month2: futureDate.toLocaleString(params.lang, { month: 'long' }), + day2: futureDate.getDate(), + year2: futureDate.getFullYear() + } + }) +} diff --git a/skills/utilities/date_time/src/lib/constants.ts b/skills/utilities/date_time/src/lib/constants.ts new file mode 100644 index 00000000..7e3e7a85 --- /dev/null +++ b/skills/utilities/date_time/src/lib/constants.ts @@ -0,0 +1 @@ +export const ONE_DAY_MILLISECONDS = 1_000 * 60 * 60 * 24 From e9245b522fdcde72bb68504cb1a7c3bfb404d319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Sun, 25 Jun 2023 22:46:10 +0200 Subject: [PATCH 07/10] fix(skill/date_time): separate current date and current time --- skills/utilities/date_time/config/en.json | 20 ++++++++++++++++--- .../date_time/src/actions/current_date.ts | 15 ++++++++++++++ .../date_time/src/actions/current_time.ts | 16 +++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 skills/utilities/date_time/src/actions/current_date.ts create mode 100644 skills/utilities/date_time/src/actions/current_time.ts diff --git a/skills/utilities/date_time/config/en.json b/skills/utilities/date_time/config/en.json index 9e2c1b02..2120d962 100644 --- a/skills/utilities/date_time/config/en.json +++ b/skills/utilities/date_time/config/en.json @@ -4,16 +4,28 @@ "current_date_time": { "type": "logic", "utterance_samples": [ - "What time is it?", + "What is the current date and time?", + "What is the date and time?" + ] + }, + "current_date": { + "type": "logic", + "utterance_samples": [ "What's today date?", - "Do you have the time?", - "Can you tell me the current time?", "What is the date today?", "What day is it?", "What day of the week are we?", "What day of the week is it?" ] }, + "current_time": { + "type": "logic", + "utterance_samples": [ + "What time is it?", + "Do you have the time?", + "Can you tell me the current time?" + ] + }, "current_week_number": { "type": "logic", "utterance_samples": [ @@ -37,6 +49,8 @@ "current_date_time": [ "It is %weekday%, %month% %day%, %year%, and it is %hours%:%minutes%:%seconds%." ], + "current_date": ["It is %weekday%, %month% %day%, %year%."], + "current_time": ["It is %hours%:%minutes%:%seconds%."], "current_week_number": ["It is the %week_number% week of the year."], "days_countdown": [ "There are %days% days between %month1% %day1%, %year1% and %month2% %day2%, %year2%." diff --git a/skills/utilities/date_time/src/actions/current_date.ts b/skills/utilities/date_time/src/actions/current_date.ts new file mode 100644 index 00000000..42a814fa --- /dev/null +++ b/skills/utilities/date_time/src/actions/current_date.ts @@ -0,0 +1,15 @@ +import type { ActionFunction } from '@sdk/types' +import { leon } from '@sdk/leon' + +export const run: ActionFunction = async function (params) { + const currentDate = new Date() + await leon.answer({ + key: 'current_date', + data: { + weekday: currentDate.toLocaleString(params.lang, { weekday: 'long' }), + month: currentDate.toLocaleString(params.lang, { month: 'long' }), + day: currentDate.getDate(), + year: currentDate.getFullYear() + } + }) +} diff --git a/skills/utilities/date_time/src/actions/current_time.ts b/skills/utilities/date_time/src/actions/current_time.ts new file mode 100644 index 00000000..f4ae75cb --- /dev/null +++ b/skills/utilities/date_time/src/actions/current_time.ts @@ -0,0 +1,16 @@ +import type { ActionFunction } from '@sdk/types' +import { leon } from '@sdk/leon' + +import { zeroPad } from '../lib/zeroPad' + +export const run: ActionFunction = async function () { + const currentDate = new Date() + await leon.answer({ + key: 'current_time', + data: { + hours: zeroPad(currentDate.getHours()), + minutes: zeroPad(currentDate.getMinutes()), + seconds: zeroPad(currentDate.getSeconds()) + } + }) +} From 1410b723627f011ed7261b0ce693c71cbd2776c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LUDWIG?= Date: Tue, 27 Jun 2023 18:35:03 +0200 Subject: [PATCH 08/10] chore(skill/date_time): add test folder --- .../date_time/src/actions/days_countdown.ts | 14 +++++--------- skills/utilities/date_time/test/.gitkeep | 0 2 files changed, 5 insertions(+), 9 deletions(-) create mode 100644 skills/utilities/date_time/test/.gitkeep diff --git a/skills/utilities/date_time/src/actions/days_countdown.ts b/skills/utilities/date_time/src/actions/days_countdown.ts index e484ed27..ae42a06c 100644 --- a/skills/utilities/date_time/src/actions/days_countdown.ts +++ b/skills/utilities/date_time/src/actions/days_countdown.ts @@ -24,26 +24,22 @@ const daysBetween = (date1: Date, date2: Date): number => { } export const run: ActionFunction = async function (params) { - const { current_entities } = params - - let futureDateString: string | null = null - - for (const entity of current_entities) { + let dateRangeEntity: BuiltInDateRangeEntity | null = null + for (const entity of params.current_entities) { if (isBuiltInDateRangeEntity(entity)) { - const { futureEndDate } = entity.resolution - futureDateString = futureEndDate + dateRangeEntity = entity break } } - if (futureDateString == null) { + if (dateRangeEntity == null) { return await leon.answer({ key: 'days_countdown_error' }) } const currentDate = new Date() - const futureDate = new Date(futureDateString) + const futureDate = new Date(dateRangeEntity.resolution.futureEndDate) const daysCountdown = daysBetween(currentDate, futureDate) await leon.answer({ key: 'days_countdown', diff --git a/skills/utilities/date_time/test/.gitkeep b/skills/utilities/date_time/test/.gitkeep new file mode 100644 index 00000000..e69de29b From f1a65f53bc17798af75e440d300daf3f10912166 Mon Sep 17 00:00:00 2001 From: louistiti Date: Wed, 28 Jun 2023 23:39:07 +0800 Subject: [PATCH 09/10] feat(skill/age): add new utterance samples --- skills/leon/age/config/en.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/skills/leon/age/config/en.json b/skills/leon/age/config/en.json index 997b2806..7d100b6a 100644 --- a/skills/leon/age/config/en.json +++ b/skills/leon/age/config/en.json @@ -9,7 +9,12 @@ "Are you young?", "When were you born?", "When have you been created?", - "When is your birthday?" + "When is your birthday?", + "What is your age?", + "When did you first come into existence?", + "What is your date of creation?", + "How long have you been in operation?", + "How many years have you been around?" ] } }, From eea6b0392da0e10326e13d1f82ec8432ee9c7ee2 Mon Sep 17 00:00:00 2001 From: louistiti Date: Thu, 29 Jun 2023 00:17:29 +0800 Subject: [PATCH 10/10] feat(skill/date_time): add new utterance samples --- core/skills-endpoints.json | 62 +++++++++++++++-------- skills/utilities/date_time/config/en.json | 13 +++-- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/core/skills-endpoints.json b/core/skills-endpoints.json index 7c39eca1..642eb152 100644 --- a/core/skills-endpoints.json +++ b/core/skills-endpoints.json @@ -68,27 +68,6 @@ "route": "/api/action/social_communication/mbti/quiz", "params": [] }, - { - "method": "GET", - "route": "/api/action/utilities/have_i_been_pwned/run", - "params": [] - }, - { - "method": "POST", - "route": "/api/action/utilities/is_it_down/run", - "params": ["url"], - "entitiesType": "builtIn" - }, - { - "method": "GET", - "route": "/api/action/utilities/speed_test/run", - "params": [] - }, - { - "method": "GET", - "route": "/api/action/utilities/timekeeper/run", - "params": [] - }, { "method": "POST", "route": "/api/action/games/akinator/choose_thematic", @@ -210,6 +189,47 @@ "method": "GET", "route": "/api/action/leon/welcome/run", "params": [] + }, + { + "method": "GET", + "route": "/api/action/utilities/date_time/current_date_time", + "params": [] + }, + { + "method": "GET", + "route": "/api/action/utilities/date_time/current_date", + "params": [] + }, + { + "method": "GET", + "route": "/api/action/utilities/date_time/current_time", + "params": [] + }, + { + "method": "GET", + "route": "/api/action/utilities/date_time/current_week_number", + "params": [] + }, + { + "method": "GET", + "route": "/api/action/utilities/date_time/days_countdown", + "params": [] + }, + { + "method": "GET", + "route": "/api/action/utilities/have_i_been_pwned/run", + "params": [] + }, + { + "method": "POST", + "route": "/api/action/utilities/is_it_down/run", + "params": ["url"], + "entitiesType": "builtIn" + }, + { + "method": "GET", + "route": "/api/action/utilities/speed_test/run", + "params": [] } ] } diff --git a/skills/utilities/date_time/config/en.json b/skills/utilities/date_time/config/en.json index 2120d962..b7328c32 100644 --- a/skills/utilities/date_time/config/en.json +++ b/skills/utilities/date_time/config/en.json @@ -5,7 +5,9 @@ "type": "logic", "utterance_samples": [ "What is the current date and time?", - "What is the date and time?" + "What is the date and time?", + "Tell me the time and date", + "What's the present date and time?" ] }, "current_date": { @@ -23,7 +25,8 @@ "utterance_samples": [ "What time is it?", "Do you have the time?", - "Can you tell me the current time?" + "Can you tell me the current time?", + "What's the time right now?" ] }, "current_week_number": { @@ -33,7 +36,8 @@ "Which week of the year is it?", "What week are we in right now?", "What is this week's number?", - "What week of the year are we?" + "What week of the year are we?", + "What's the current week number" ] }, "days_countdown": { @@ -41,7 +45,8 @@ "utterance_samples": [ "How many days left until 1st July?", "What is the number of days remaining until December 25th?", - "In how many days will it be February 10th?" + "In how many days will it be February 10th?", + "Calculate the number until March 1st" ] } },