mirror of
https://github.com/leon-ai/leon.git
synced 2024-12-24 09:12:20 +03:00
Merge branch 'nodejs-bridge' into develop
This commit is contained in:
commit
2c2f49290a
@ -11,5 +11,8 @@
|
||||
"homepage": "https://getleon.ai",
|
||||
"bugs": {
|
||||
"url": "https://github.com/leon-ai/leon/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "1.4.0"
|
||||
}
|
||||
}
|
||||
|
40
bridges/nodejs/src/constants.ts
Normal file
40
bridges/nodejs/src/constants.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import type { SkillConfigSchema } from '@server/schemas/skill-schemas'
|
||||
|
||||
import type { IntentObject } from '@sdk/types'
|
||||
|
||||
const {
|
||||
argv: [, , INTENT_OBJ_FILE_PATH]
|
||||
} = process
|
||||
|
||||
export const INTENT_OBJECT: IntentObject = JSON.parse(
|
||||
fs.readFileSync(INTENT_OBJ_FILE_PATH as string, 'utf8')
|
||||
)
|
||||
export const SKILL_CONFIG: SkillConfigSchema = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(
|
||||
process.cwd(),
|
||||
'skills',
|
||||
INTENT_OBJECT.domain,
|
||||
INTENT_OBJECT.skill,
|
||||
'config',
|
||||
INTENT_OBJECT.lang + '.json'
|
||||
),
|
||||
'utf8'
|
||||
)
|
||||
)
|
||||
export const SKILL_SRC_CONFIG: Record<string, unknown> = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(
|
||||
process.cwd(),
|
||||
'skills',
|
||||
INTENT_OBJECT.domain,
|
||||
INTENT_OBJECT.skill,
|
||||
'src',
|
||||
'config.json'
|
||||
),
|
||||
'utf8'
|
||||
)
|
||||
).configurations
|
@ -1,5 +1,47 @@
|
||||
import { VERSION } from './version'
|
||||
import path from 'node:path'
|
||||
|
||||
console.log('[WIP] Node.js bridge', VERSION)
|
||||
import type { ActionFunction, ActionParams } from '@sdk/types'
|
||||
import { INTENT_OBJECT } from '@bridge/constants'
|
||||
;(async (): Promise<void> => {
|
||||
const {
|
||||
domain,
|
||||
skill,
|
||||
action,
|
||||
lang,
|
||||
utterance,
|
||||
current_entities,
|
||||
entities,
|
||||
current_resolvers,
|
||||
resolvers,
|
||||
slots
|
||||
} = INTENT_OBJECT
|
||||
|
||||
// TODO
|
||||
const params: ActionParams = {
|
||||
lang,
|
||||
utterance,
|
||||
current_entities,
|
||||
entities,
|
||||
current_resolvers,
|
||||
resolvers,
|
||||
slots
|
||||
}
|
||||
|
||||
try {
|
||||
const actionModule = await import(
|
||||
path.join(
|
||||
process.cwd(),
|
||||
'skills',
|
||||
domain,
|
||||
skill,
|
||||
'src',
|
||||
'actions',
|
||||
`${action}.ts`
|
||||
)
|
||||
)
|
||||
const actionFunction: ActionFunction = actionModule[action]
|
||||
|
||||
await actionFunction(params)
|
||||
} catch (e) {
|
||||
console.error(`Error while running "${skill}" skill "${action}" action:`, e)
|
||||
}
|
||||
})()
|
||||
|
6
bridges/nodejs/src/sdk/aurora/button.ts
Normal file
6
bridges/nodejs/src/sdk/aurora/button.ts
Normal file
@ -0,0 +1,6 @@
|
||||
// TODO: contains the button API. rendering engine <-> SDK
|
||||
export class Button {
|
||||
constructor() {
|
||||
console.log('Button constructor')
|
||||
}
|
||||
}
|
5
bridges/nodejs/src/sdk/aurora/index.ts
Normal file
5
bridges/nodejs/src/sdk/aurora/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { Button } from './button'
|
||||
|
||||
export default {
|
||||
Button
|
||||
}
|
112
bridges/nodejs/src/sdk/leon.ts
Normal file
112
bridges/nodejs/src/sdk/leon.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import type { AnswerData, AnswerInput, AnswerOutput } from '@sdk/types'
|
||||
import {
|
||||
INTENT_OBJECT,
|
||||
SKILL_CONFIG,
|
||||
SKILL_SRC_CONFIG
|
||||
} from '@bridge/constants'
|
||||
|
||||
class Leon {
|
||||
private static instance: Leon
|
||||
|
||||
constructor() {
|
||||
if (!Leon.instance) {
|
||||
Leon.instance = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source configuration
|
||||
* @example getSRCConfig() // { credentials: { apiKey: 'abc' } }
|
||||
*/
|
||||
public getSRCConfig<T>(key: string): T
|
||||
public getSRCConfig<T extends Record<string, unknown>>(key?: undefined): T
|
||||
public getSRCConfig<T extends Record<string, unknown> | unknown>(
|
||||
key?: string
|
||||
): T {
|
||||
try {
|
||||
if (key) {
|
||||
return SKILL_SRC_CONFIG[key] as T
|
||||
}
|
||||
|
||||
return SKILL_SRC_CONFIG as T
|
||||
} catch (e) {
|
||||
console.error('Error while getting source configuration:', e)
|
||||
|
||||
return {} as T
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply data to the answer
|
||||
* @param answerKey The answer key
|
||||
* @param data The data to apply
|
||||
* @example setAnswerData('key', { name: 'Leon' })
|
||||
*/
|
||||
public setAnswerData(
|
||||
answerKey: string,
|
||||
data: AnswerData = null
|
||||
): string | null {
|
||||
try {
|
||||
// In case the answer key is a raw answer
|
||||
if (SKILL_CONFIG.answers == null || !SKILL_CONFIG.answers[answerKey]) {
|
||||
return answerKey
|
||||
}
|
||||
|
||||
const answers = SKILL_CONFIG.answers[answerKey] ?? ''
|
||||
let answer: string
|
||||
|
||||
if (Array.isArray(answers)) {
|
||||
answer = answers[Math.floor(Math.random() * answers.length)] ?? ''
|
||||
} else {
|
||||
answer = answers
|
||||
}
|
||||
|
||||
if (data) {
|
||||
for (const key in data) {
|
||||
answer = answer.replaceAll(`%${key}%`, String(data[key]))
|
||||
}
|
||||
}
|
||||
|
||||
if (SKILL_CONFIG.variables) {
|
||||
const { variables } = SKILL_CONFIG
|
||||
|
||||
for (const key in variables) {
|
||||
answer = answer.replaceAll(`%${key}%`, String(variables[key]))
|
||||
}
|
||||
}
|
||||
|
||||
return answer
|
||||
} catch (e) {
|
||||
console.error('Error while setting answer data:', e)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an answer to the core
|
||||
* @param answerInput The answer input
|
||||
* @example answer({ key: 'greet' }) // 'Hello world'
|
||||
* @example answer({ key: 'welcome', data: { name: 'Louis' } }) // 'Welcome Louis'
|
||||
* @example answer({ key: 'confirm', core: { restart: true } }) // 'Would you like to retry?'
|
||||
*/
|
||||
public async answer(answerInput: AnswerInput): Promise<void> {
|
||||
try {
|
||||
const answerObject: AnswerOutput = {
|
||||
...INTENT_OBJECT,
|
||||
output: {
|
||||
codes: answerInput.key,
|
||||
speech: this.setAnswerData(answerInput.key, answerInput.data) ?? '',
|
||||
core: answerInput.core,
|
||||
options: this.getSRCConfig('options')
|
||||
}
|
||||
}
|
||||
|
||||
process.stdout.write(JSON.stringify(answerObject))
|
||||
} catch (e) {
|
||||
console.error('Error while creating answer:', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const leon = new Leon()
|
96
bridges/nodejs/src/sdk/network.ts
Normal file
96
bridges/nodejs/src/sdk/network.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import axios from 'axios'
|
||||
import type { AxiosInstance } from 'axios'
|
||||
|
||||
interface NetworkOptions {
|
||||
/** `baseURL` will be prepended to `url`. It can be convenient to set `baseURL` for an instance of `Network` to pass relative URLs. */
|
||||
baseURL?: string
|
||||
}
|
||||
|
||||
interface NetworkRequestOptions {
|
||||
/** Server URL that will be used for the request. */
|
||||
url: string
|
||||
|
||||
/** Request method to be used when making the request. */
|
||||
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
||||
|
||||
/** Data to be sent as the request body. */
|
||||
data?: Record<string, unknown>
|
||||
|
||||
/** Custom headers to be sent. */
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
|
||||
interface NetworkResponse<ResponseData> {
|
||||
/** Data provided by the server. */
|
||||
data: ResponseData
|
||||
|
||||
/** HTTP status code from the server response. */
|
||||
statusCode: number
|
||||
|
||||
/** Options that was provided for the request. */
|
||||
options: NetworkRequestOptions & NetworkOptions
|
||||
}
|
||||
|
||||
export class NetworkError<ResponseErrorData = unknown> extends Error {
|
||||
public readonly response: NetworkResponse<ResponseErrorData>
|
||||
|
||||
public constructor(response: NetworkResponse<ResponseErrorData>) {
|
||||
super(`[NetworkError]: ${response.statusCode}`)
|
||||
this.response = response
|
||||
Object.setPrototypeOf(this, NetworkError.prototype)
|
||||
}
|
||||
}
|
||||
|
||||
export class Network {
|
||||
private options: NetworkOptions
|
||||
private axios: AxiosInstance
|
||||
|
||||
public constructor(options: NetworkOptions = {}) {
|
||||
this.options = options
|
||||
this.axios = axios.create({
|
||||
baseURL: this.options.baseURL
|
||||
})
|
||||
}
|
||||
|
||||
public async request<ResponseData = unknown, ResponseErrorData = unknown>(
|
||||
options: NetworkRequestOptions
|
||||
): Promise<NetworkResponse<ResponseData>> {
|
||||
try {
|
||||
const response = await this.axios.request<ResponseData>({
|
||||
url: options.url,
|
||||
method: options.method.toLowerCase(),
|
||||
data: options.data,
|
||||
headers: options.headers
|
||||
})
|
||||
return {
|
||||
data: response.data,
|
||||
statusCode: response.status,
|
||||
options: {
|
||||
...this.options,
|
||||
...options
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
let statusCode = 500
|
||||
let data = {} as ResponseErrorData
|
||||
if (axios.isAxiosError(error)) {
|
||||
data = error?.response?.data
|
||||
statusCode = error?.response?.status ?? 500
|
||||
}
|
||||
throw new NetworkError<ResponseErrorData>({
|
||||
data,
|
||||
statusCode,
|
||||
options: {
|
||||
...this.options,
|
||||
...options
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public isNetworkError<ResponseErrorData = unknown>(
|
||||
error: unknown
|
||||
): error is NetworkError<ResponseErrorData> {
|
||||
return error instanceof NetworkError
|
||||
}
|
||||
}
|
39
bridges/nodejs/src/sdk/types.ts
Normal file
39
bridges/nodejs/src/sdk/types.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Action types
|
||||
*/
|
||||
import type { ActionParams, IntentObject } from '@/core/brain/types'
|
||||
|
||||
export type { ActionParams, IntentObject }
|
||||
|
||||
export type ActionFunction = (params: ActionParams) => Promise<void>
|
||||
|
||||
/**
|
||||
* Answer types
|
||||
*/
|
||||
export interface AnswerOutput extends IntentObject {
|
||||
output: {
|
||||
codes: string
|
||||
speech: string
|
||||
core?: AnswerCoreData
|
||||
options: Record<string, string>
|
||||
}
|
||||
}
|
||||
export interface AnswerCoreData {
|
||||
restart?: boolean
|
||||
isInActionLoop?: boolean
|
||||
showNextActionSuggestions?: boolean
|
||||
showSuggestions?: boolean
|
||||
}
|
||||
export interface TextAnswer {
|
||||
key: string
|
||||
data?: AnswerData
|
||||
core?: AnswerCoreData
|
||||
}
|
||||
export interface WidgetAnswer {
|
||||
// TODO
|
||||
key: 'widget'
|
||||
data?: AnswerData
|
||||
core?: AnswerCoreData
|
||||
}
|
||||
export type AnswerData = Record<string, string | number> | null
|
||||
export type AnswerInput = TextAnswer | WidgetAnswer
|
@ -1,23 +1,24 @@
|
||||
{
|
||||
"extends": "@tsconfig/node16-strictest/tsconfig.json",
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist/bin",
|
||||
"rootDir": "../../",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@@/*": ["../../*"],
|
||||
"@/*": ["../../server/src/*"],
|
||||
"@server/*": ["../../server/src/*"],
|
||||
"@bridge/*": ["./src/*"],
|
||||
"@sdk/*": ["./src/sdk/*"]
|
||||
},
|
||||
"ignoreDeprecations": "5.0",
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"resolveJsonModule": true
|
||||
"exactOptionalPropertyTypes": false,
|
||||
"declaration": true
|
||||
},
|
||||
"ts-node": {
|
||||
"swc": true,
|
||||
"require": ["tsconfig-paths/register"],
|
||||
"files": true
|
||||
},
|
||||
"files": [],
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["dist"]
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ def translate(key, dict = { }):
|
||||
"""Pickup the language file according to the cmd arg
|
||||
and return the value according to the params"""
|
||||
|
||||
# "Temporize" for the data buffer ouput on the core
|
||||
# "Temporize" for the data buffer output on the core
|
||||
sleep(0.1)
|
||||
|
||||
output = ''
|
||||
@ -82,6 +82,7 @@ def output(type, content = '', core = { }):
|
||||
'entities': intent_obj['entities'],
|
||||
'slots': intent_obj['slots'],
|
||||
'output': {
|
||||
# TODO: remove type as it is not needed anymore
|
||||
'type': type,
|
||||
'codes': codes,
|
||||
'speech': speech,
|
||||
@ -90,8 +91,7 @@ def output(type, content = '', core = { }):
|
||||
}
|
||||
}))
|
||||
|
||||
if (type == 'inter'):
|
||||
sys.stdout.flush()
|
||||
sys.stdout.flush()
|
||||
|
||||
def http(method, url, headers = None):
|
||||
"""Send HTTP request with the Leon user agent"""
|
||||
|
@ -141,6 +141,11 @@
|
||||
"route": "/api/action/games/rochambeau/rematch",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/leon/age/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/leon/color/favorite_color",
|
||||
|
@ -117,6 +117,7 @@
|
||||
"@types/node-wav": "0.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.55.0",
|
||||
"@typescript-eslint/parser": "5.55.0",
|
||||
"@vercel/ncc": "0.36.1",
|
||||
"cli-spinner": "0.2.10",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
|
12
scripts/assets/nodejs-bridge-intent-object.json
Normal file
12
scripts/assets/nodejs-bridge-intent-object.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"lang": "en",
|
||||
"domain": "leon",
|
||||
"skill": "age",
|
||||
"action": "run",
|
||||
"utterance": "How old are you?",
|
||||
"slots": {},
|
||||
"entities": [],
|
||||
"current_entities": [],
|
||||
"resolvers": [],
|
||||
"current_resolvers": []
|
||||
}
|
@ -137,19 +137,25 @@ BUILD_TARGETS.set('tcp-server', {
|
||||
* Build for binaries not requiring a Python environment
|
||||
*/
|
||||
try {
|
||||
const tsconfigPath = path.join(NODEJS_BRIDGE_ROOT_PATH, 'tsconfig.json')
|
||||
const distBinPath = path.join(NODEJS_BRIDGE_DIST_PATH, 'bin')
|
||||
const distMainFilePath = path.join(
|
||||
NODEJS_BRIDGE_DIST_PATH,
|
||||
'bin',
|
||||
'main.js'
|
||||
distBinPath,
|
||||
'index.js'
|
||||
)
|
||||
const distRenamedMainFilePath = path.join(
|
||||
NODEJS_BRIDGE_DIST_PATH,
|
||||
'bin',
|
||||
distBinPath,
|
||||
NODEJS_BRIDGE_BIN_NAME
|
||||
)
|
||||
|
||||
await command(`tsc --project ${tsconfigPath}`, {
|
||||
await fs.promises.rm(buildPath, { recursive: true, force: true })
|
||||
|
||||
const inputMainFilePath = path.join(
|
||||
NODEJS_BRIDGE_ROOT_PATH,
|
||||
'src',
|
||||
'main.ts'
|
||||
)
|
||||
|
||||
await command(`ncc build ${inputMainFilePath} --out ${distBinPath}`, {
|
||||
shell: true,
|
||||
stdio: 'inherit'
|
||||
})
|
||||
|
@ -16,9 +16,11 @@ import { SystemHelper } from '@/helpers/system-helper'
|
||||
import {
|
||||
MINIMUM_REQUIRED_RAM,
|
||||
LEON_VERSION,
|
||||
NODEJS_BRIDGE_BIN_PATH,
|
||||
PYTHON_BRIDGE_BIN_PATH,
|
||||
TCP_SERVER_BIN_PATH,
|
||||
TCP_SERVER_VERSION,
|
||||
NODEJS_BRIDGE_VERSION,
|
||||
PYTHON_BRIDGE_VERSION,
|
||||
INSTANCE_ID
|
||||
} from '@/constants'
|
||||
@ -102,6 +104,13 @@ dotenv.config()
|
||||
skillsResolversModelState: null,
|
||||
mainModelState: null
|
||||
},
|
||||
nodeJSBridge: {
|
||||
version: null,
|
||||
executionTime: null,
|
||||
command: null,
|
||||
output: null,
|
||||
error: null
|
||||
},
|
||||
pythonBridge: {
|
||||
version: null,
|
||||
executionTime: null,
|
||||
@ -209,7 +218,41 @@ dotenv.config()
|
||||
})
|
||||
|
||||
/**
|
||||
* Skill execution checking
|
||||
* Skill execution checking with Node.js bridge
|
||||
*/
|
||||
|
||||
LogHelper.success(`Node.js bridge version: ${NODEJS_BRIDGE_VERSION}`)
|
||||
reportDataInput.nodeJSBridge.version = NODEJS_BRIDGE_VERSION
|
||||
LogHelper.info('Executing a skill...')
|
||||
|
||||
try {
|
||||
const executionStart = Date.now()
|
||||
const p = await command(
|
||||
`${NODEJS_BRIDGE_BIN_PATH} "${path.join(
|
||||
process.cwd(),
|
||||
'scripts',
|
||||
'assets',
|
||||
'nodejs-bridge-intent-object.json'
|
||||
)}"`,
|
||||
{ shell: true }
|
||||
)
|
||||
const executionEnd = Date.now()
|
||||
const executionTime = executionEnd - executionStart
|
||||
LogHelper.info(p.command)
|
||||
reportDataInput.nodeJSBridge.command = p.command
|
||||
LogHelper.success(p.stdout)
|
||||
reportDataInput.nodeJSBridge.output = p.stdout
|
||||
LogHelper.info(`Skill execution time: ${executionTime}ms\n`)
|
||||
reportDataInput.nodeJSBridge.executionTime = `${executionTime}ms`
|
||||
} catch (e) {
|
||||
LogHelper.info(e.command)
|
||||
report.can_run_skill.v = false
|
||||
LogHelper.error(`${e}\n`)
|
||||
reportDataInput.nodeJSBridge.error = JSON.stringify(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Skill execution checking with Python bridge
|
||||
*/
|
||||
|
||||
LogHelper.success(`Python bridge version: ${PYTHON_BRIDGE_VERSION}`)
|
||||
@ -223,7 +266,7 @@ dotenv.config()
|
||||
process.cwd(),
|
||||
'scripts',
|
||||
'assets',
|
||||
'intent-object.json'
|
||||
'python-bridge-intent-object.json'
|
||||
)}"`,
|
||||
{ shell: true }
|
||||
)
|
||||
|
@ -72,11 +72,13 @@ export const PYTHON_BRIDGE_BIN_PATH = path.join(
|
||||
BINARIES_FOLDER_NAME,
|
||||
PYTHON_BRIDGE_BIN_NAME
|
||||
)
|
||||
export const NODEJS_BRIDGE_BIN_PATH = `${process.execPath} ${path.join(
|
||||
NODEJS_BRIDGE_DIST_PATH,
|
||||
'bin',
|
||||
NODEJS_BRIDGE_BIN_NAME
|
||||
)}`
|
||||
export const NODEJS_BRIDGE_BIN_PATH = `${path.join(
|
||||
process.cwd(),
|
||||
'node_modules',
|
||||
'ts-node',
|
||||
'dist',
|
||||
'bin.js'
|
||||
)} --swc ${path.join(NODEJS_BRIDGE_DIST_PATH, 'bin', NODEJS_BRIDGE_BIN_NAME)}`
|
||||
|
||||
export const LEON_VERSION = process.env['npm_package_version']
|
||||
|
||||
|
@ -15,11 +15,7 @@ import type {
|
||||
IntentObject,
|
||||
SkillResult
|
||||
} from '@/core/brain/types'
|
||||
import {
|
||||
SkillActionTypes,
|
||||
SkillBridges,
|
||||
SkillOutputTypes
|
||||
} from '@/core/brain/types'
|
||||
import { SkillActionTypes, SkillBridges } from '@/core/brain/types'
|
||||
import { langs } from '@@/core/langs.json'
|
||||
import {
|
||||
HAS_TTS,
|
||||
@ -199,18 +195,15 @@ export default class Brain {
|
||||
const obj = JSON.parse(data.toString())
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
if (obj.output.type === SkillOutputTypes.Intermediate) {
|
||||
LogHelper.title(`${this.skillFriendlyName} skill`)
|
||||
LogHelper.info(data.toString())
|
||||
LogHelper.title(`${this.skillFriendlyName} skill (on data)`)
|
||||
LogHelper.info(data.toString())
|
||||
|
||||
const speech = obj.output.speech.toString()
|
||||
if (!this.isMuted) {
|
||||
this.talk(speech)
|
||||
}
|
||||
this.speeches.push(speech)
|
||||
} else {
|
||||
this.skillOutput = data.toString()
|
||||
const speech = obj.output.speech.toString()
|
||||
if (!this.isMuted) {
|
||||
this.talk(speech)
|
||||
}
|
||||
this.speeches.push(speech)
|
||||
this.skillOutput = data.toString()
|
||||
|
||||
return Promise.resolve(null)
|
||||
} else {
|
||||
@ -388,7 +381,7 @@ export default class Brain {
|
||||
|
||||
// Catch the end of the skill execution
|
||||
this.skillProcess?.stdout.on('end', () => {
|
||||
LogHelper.title(`${this.skillFriendlyName} skill`)
|
||||
LogHelper.title(`${this.skillFriendlyName} skill (on end)`)
|
||||
LogHelper.info(this.skillOutput)
|
||||
|
||||
let skillResult: SkillResult | undefined = undefined
|
||||
@ -398,36 +391,26 @@ export default class Brain {
|
||||
try {
|
||||
skillResult = JSON.parse(this.skillOutput)
|
||||
|
||||
if (skillResult?.output.speech) {
|
||||
skillResult.output.speech =
|
||||
skillResult.output.speech.toString()
|
||||
if (!this.isMuted) {
|
||||
this.talk(skillResult.output.speech, true)
|
||||
}
|
||||
speeches.push(skillResult.output.speech)
|
||||
// Synchronize the downloaded content if enabled
|
||||
if (
|
||||
skillResult &&
|
||||
skillResult.output.options['synchronization'] &&
|
||||
skillResult.output.options['synchronization'].enabled &&
|
||||
skillResult.output.options['synchronization'].enabled === true
|
||||
) {
|
||||
const sync = new Synchronizer(
|
||||
this,
|
||||
nluResult.classification,
|
||||
skillResult.output.options['synchronization']
|
||||
)
|
||||
|
||||
// Synchronize the downloaded content if enabled
|
||||
if (
|
||||
skillResult.output.type === SkillOutputTypes.End &&
|
||||
skillResult.output.options['synchronization'] &&
|
||||
skillResult.output.options['synchronization'].enabled &&
|
||||
skillResult.output.options['synchronization'].enabled ===
|
||||
true
|
||||
) {
|
||||
const sync = new Synchronizer(
|
||||
this,
|
||||
nluResult.classification,
|
||||
skillResult.output.options['synchronization']
|
||||
)
|
||||
|
||||
// When the synchronization is finished
|
||||
sync.synchronize((speech: string) => {
|
||||
if (!this.isMuted) {
|
||||
this.talk(speech)
|
||||
}
|
||||
speeches.push(speech)
|
||||
})
|
||||
}
|
||||
// When the synchronization is finished
|
||||
sync.synchronize((speech: string) => {
|
||||
if (!this.isMuted) {
|
||||
this.talk(speech)
|
||||
}
|
||||
speeches.push(speech)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
LogHelper.title(`${this.skillFriendlyName} skill`)
|
||||
|
@ -28,7 +28,6 @@ export interface SkillResult {
|
||||
entities: NEREntity[]
|
||||
slots: NLUSlots
|
||||
output: {
|
||||
type: SkillOutputTypes
|
||||
codes: string[]
|
||||
speech: string
|
||||
core: SkillCoreData | undefined
|
||||
@ -41,21 +40,13 @@ export enum SkillBridges {
|
||||
Python = 'python',
|
||||
NodeJS = 'nodejs'
|
||||
}
|
||||
export enum SkillOutputTypes {
|
||||
Intermediate = 'inter',
|
||||
End = 'end'
|
||||
}
|
||||
export enum SkillActionTypes {
|
||||
Logic = 'logic',
|
||||
Dialog = 'dialog'
|
||||
}
|
||||
|
||||
export interface IntentObject {
|
||||
id: string
|
||||
export interface ActionParams {
|
||||
lang: ShortLanguageCode
|
||||
domain: NLPDomain
|
||||
skill: NLPSkill
|
||||
action: NLPAction
|
||||
utterance: NLPUtterance
|
||||
current_entities: NEREntity[]
|
||||
entities: NEREntity[]
|
||||
@ -64,6 +55,13 @@ export interface IntentObject {
|
||||
slots: { [key: string]: NLUSlot['value'] | undefined }
|
||||
}
|
||||
|
||||
export interface IntentObject extends ActionParams {
|
||||
id: string
|
||||
domain: NLPDomain
|
||||
skill: NLPSkill
|
||||
action: NLPAction
|
||||
}
|
||||
|
||||
export interface BrainProcessResult extends NLUResult {
|
||||
speeches: string[]
|
||||
executionTime: number
|
||||
|
@ -8,7 +8,7 @@ enum OSNames {
|
||||
Linux = 'Linux',
|
||||
Unknown = 'Unknown'
|
||||
}
|
||||
enum BinaryFolderNames {
|
||||
export enum BinaryFolderNames {
|
||||
Linux64Bit = 'linux-x86_64', // Linux 64-bit (Intel)
|
||||
LinuxARM64 = 'linux-aarch64', // Linux 64-bit (ARM)
|
||||
MacOS64Bit = 'macosx-x86_64', // Apple 64-bit (Intel)
|
||||
|
0
skills/leon/age/README.md
Normal file
0
skills/leon/age/README.md
Normal file
14
skills/leon/age/config/en.json
Normal file
14
skills/leon/age/config/en.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "../../../../schemas/skill-schemas/skill-config.json",
|
||||
"actions": {
|
||||
"run": {
|
||||
"type": "logic",
|
||||
"utterance_samples": ["How old are you?"]
|
||||
}
|
||||
},
|
||||
"answers": {
|
||||
"default": ["I'm..."],
|
||||
"greet": ["Hey, just a try %name% again %name%", "Another try, hi"],
|
||||
"answer": ["%answer%"]
|
||||
}
|
||||
}
|
0
skills/leon/age/memory/.gitkeep
Normal file
0
skills/leon/age/memory/.gitkeep
Normal file
12
skills/leon/age/skill.json
Normal file
12
skills/leon/age/skill.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "../../../schemas/skill-schemas/skill.json",
|
||||
"name": "Age",
|
||||
"bridge": "nodejs",
|
||||
"version": "1.0.0",
|
||||
"description": "Leon tells his age.",
|
||||
"author": {
|
||||
"name": "Louis Grenard",
|
||||
"email": "louis@getleon.ai",
|
||||
"url": "https://github.com/louistiti"
|
||||
}
|
||||
}
|
62
skills/leon/age/src/actions/run.ts
Normal file
62
skills/leon/age/src/actions/run.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import type { ActionFunction } from '@sdk/types'
|
||||
import { leon } from '@sdk/leon'
|
||||
import { Network } from '@sdk/network'
|
||||
import { Button } from '@sdk/aurora/button'
|
||||
|
||||
export const run: ActionFunction = async function () {
|
||||
await leon.answer({ key: 'default' })
|
||||
|
||||
await leon.answer({
|
||||
key: 'greet',
|
||||
data: {
|
||||
name: 'Louis'
|
||||
}
|
||||
})
|
||||
|
||||
console.log('button', Button)
|
||||
|
||||
const { someSampleConfig } = leon.getSRCConfig<{
|
||||
options: { someSampleConfig: string }
|
||||
}>()['options']
|
||||
|
||||
const options = leon.getSRCConfig<{ someSampleConfig: string }>('options')
|
||||
await leon.answer({
|
||||
key: 'answer',
|
||||
data: {
|
||||
answer: options.someSampleConfig + someSampleConfig
|
||||
}
|
||||
})
|
||||
|
||||
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}`
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
6
skills/leon/age/src/config.sample.json
Normal file
6
skills/leon/age/src/config.sample.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"configurations": {
|
||||
"options": {},
|
||||
"credentials": {}
|
||||
}
|
||||
}
|
0
skills/leon/age/src/lib/.gitkeep
Normal file
0
skills/leon/age/src/lib/.gitkeep
Normal file
0
skills/leon/age/test/.gitkeep
Normal file
0
skills/leon/age/test/.gitkeep
Normal file
9
skills/tsconfig.json
Normal file
9
skills/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@tsconfig/node16-strictest/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@bridge/*": ["../bridges/nodejs/src/*"],
|
||||
"@sdk/*": ["../bridges/nodejs/src/sdk/*"]
|
||||
}
|
||||
}
|
||||
}
|
@ -6,12 +6,15 @@
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@@/*": ["./*"],
|
||||
"@/*": ["./server/src/*"]
|
||||
"@/*": ["./server/src/*"],
|
||||
"@bridge/*": ["./bridges/nodejs/src/*"],
|
||||
"@sdk/*": ["./bridges/nodejs/src/sdk/*"]
|
||||
},
|
||||
"ignoreDeprecations": "5.0",
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"resolveJsonModule": true
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true
|
||||
},
|
||||
"ts-node": {
|
||||
"swc": true,
|
||||
@ -20,5 +23,5 @@
|
||||
},
|
||||
"files": ["server/src/global.d.ts"],
|
||||
"include": ["server/src/**/*"],
|
||||
"exclude": ["node_modules", "server/dist", "bridges/python", "tcp_server"]
|
||||
"exclude": ["node_modules", "server/dist", "bridges", "tcp_server"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user