1
1
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:
louistiti 2023-05-07 21:28:28 +08:00
commit 2c2f49290a
No known key found for this signature in database
GPG Key ID: 92CD6A2E497E1669
30 changed files with 588 additions and 88 deletions

View File

@ -11,5 +11,8 @@
"homepage": "https://getleon.ai", "homepage": "https://getleon.ai",
"bugs": { "bugs": {
"url": "https://github.com/leon-ai/leon/issues" "url": "https://github.com/leon-ai/leon/issues"
},
"dependencies": {
"axios": "1.4.0"
} }
} }

View 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

View File

@ -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)
}
})()

View File

@ -0,0 +1,6 @@
// TODO: contains the button API. rendering engine <-> SDK
export class Button {
constructor() {
console.log('Button constructor')
}
}

View File

@ -0,0 +1,5 @@
import { Button } from './button'
export default {
Button
}

View 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()

View 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
}
}

View 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

View File

@ -1,23 +1,24 @@
{ {
"extends": "@tsconfig/node16-strictest/tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"rootDir": "./src",
"outDir": "./dist/bin", "outDir": "./dist/bin",
"rootDir": "../../",
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["./src/*"] "@@/*": ["../../*"],
"@/*": ["../../server/src/*"],
"@server/*": ["../../server/src/*"],
"@bridge/*": ["./src/*"],
"@sdk/*": ["./src/sdk/*"]
}, },
"ignoreDeprecations": "5.0", "exactOptionalPropertyTypes": false,
"allowJs": true, "declaration": true
"checkJs": false,
"resolveJsonModule": true
}, },
"ts-node": { "ts-node": {
"swc": true, "swc": true,
"require": ["tsconfig-paths/register"], "require": ["tsconfig-paths/register"],
"files": true "files": true
}, },
"files": [],
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["dist"] "exclude": ["node_modules", "dist"]
} }

View File

@ -30,7 +30,7 @@ def translate(key, dict = { }):
"""Pickup the language file according to the cmd arg """Pickup the language file according to the cmd arg
and return the value according to the params""" 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) sleep(0.1)
output = '' output = ''
@ -82,6 +82,7 @@ def output(type, content = '', core = { }):
'entities': intent_obj['entities'], 'entities': intent_obj['entities'],
'slots': intent_obj['slots'], 'slots': intent_obj['slots'],
'output': { 'output': {
# TODO: remove type as it is not needed anymore
'type': type, 'type': type,
'codes': codes, 'codes': codes,
'speech': speech, '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): def http(method, url, headers = None):
"""Send HTTP request with the Leon user agent""" """Send HTTP request with the Leon user agent"""

View File

@ -141,6 +141,11 @@
"route": "/api/action/games/rochambeau/rematch", "route": "/api/action/games/rochambeau/rematch",
"params": [] "params": []
}, },
{
"method": "GET",
"route": "/api/action/leon/age/run",
"params": []
},
{ {
"method": "GET", "method": "GET",
"route": "/api/action/leon/color/favorite_color", "route": "/api/action/leon/color/favorite_color",

View File

@ -117,6 +117,7 @@
"@types/node-wav": "0.0.0", "@types/node-wav": "0.0.0",
"@typescript-eslint/eslint-plugin": "5.55.0", "@typescript-eslint/eslint-plugin": "5.55.0",
"@typescript-eslint/parser": "5.55.0", "@typescript-eslint/parser": "5.55.0",
"@vercel/ncc": "0.36.1",
"cli-spinner": "0.2.10", "cli-spinner": "0.2.10",
"eslint": "8.22.0", "eslint": "8.22.0",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.5.0",

View 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": []
}

View File

@ -137,19 +137,25 @@ BUILD_TARGETS.set('tcp-server', {
* Build for binaries not requiring a Python environment * Build for binaries not requiring a Python environment
*/ */
try { try {
const tsconfigPath = path.join(NODEJS_BRIDGE_ROOT_PATH, 'tsconfig.json') const distBinPath = path.join(NODEJS_BRIDGE_DIST_PATH, 'bin')
const distMainFilePath = path.join( const distMainFilePath = path.join(
NODEJS_BRIDGE_DIST_PATH, distBinPath,
'bin', 'index.js'
'main.js'
) )
const distRenamedMainFilePath = path.join( const distRenamedMainFilePath = path.join(
NODEJS_BRIDGE_DIST_PATH, distBinPath,
'bin',
NODEJS_BRIDGE_BIN_NAME 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, shell: true,
stdio: 'inherit' stdio: 'inherit'
}) })

View File

@ -16,9 +16,11 @@ import { SystemHelper } from '@/helpers/system-helper'
import { import {
MINIMUM_REQUIRED_RAM, MINIMUM_REQUIRED_RAM,
LEON_VERSION, LEON_VERSION,
NODEJS_BRIDGE_BIN_PATH,
PYTHON_BRIDGE_BIN_PATH, PYTHON_BRIDGE_BIN_PATH,
TCP_SERVER_BIN_PATH, TCP_SERVER_BIN_PATH,
TCP_SERVER_VERSION, TCP_SERVER_VERSION,
NODEJS_BRIDGE_VERSION,
PYTHON_BRIDGE_VERSION, PYTHON_BRIDGE_VERSION,
INSTANCE_ID INSTANCE_ID
} from '@/constants' } from '@/constants'
@ -102,6 +104,13 @@ dotenv.config()
skillsResolversModelState: null, skillsResolversModelState: null,
mainModelState: null mainModelState: null
}, },
nodeJSBridge: {
version: null,
executionTime: null,
command: null,
output: null,
error: null
},
pythonBridge: { pythonBridge: {
version: null, version: null,
executionTime: 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}`) LogHelper.success(`Python bridge version: ${PYTHON_BRIDGE_VERSION}`)
@ -223,7 +266,7 @@ dotenv.config()
process.cwd(), process.cwd(),
'scripts', 'scripts',
'assets', 'assets',
'intent-object.json' 'python-bridge-intent-object.json'
)}"`, )}"`,
{ shell: true } { shell: true }
) )

View File

@ -72,11 +72,13 @@ export const PYTHON_BRIDGE_BIN_PATH = path.join(
BINARIES_FOLDER_NAME, BINARIES_FOLDER_NAME,
PYTHON_BRIDGE_BIN_NAME PYTHON_BRIDGE_BIN_NAME
) )
export const NODEJS_BRIDGE_BIN_PATH = `${process.execPath} ${path.join( export const NODEJS_BRIDGE_BIN_PATH = `${path.join(
NODEJS_BRIDGE_DIST_PATH, process.cwd(),
'bin', 'node_modules',
NODEJS_BRIDGE_BIN_NAME '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'] export const LEON_VERSION = process.env['npm_package_version']

View File

@ -15,11 +15,7 @@ import type {
IntentObject, IntentObject,
SkillResult SkillResult
} from '@/core/brain/types' } from '@/core/brain/types'
import { import { SkillActionTypes, SkillBridges } from '@/core/brain/types'
SkillActionTypes,
SkillBridges,
SkillOutputTypes
} from '@/core/brain/types'
import { langs } from '@@/core/langs.json' import { langs } from '@@/core/langs.json'
import { import {
HAS_TTS, HAS_TTS,
@ -199,18 +195,15 @@ export default class Brain {
const obj = JSON.parse(data.toString()) const obj = JSON.parse(data.toString())
if (typeof obj === 'object') { if (typeof obj === 'object') {
if (obj.output.type === SkillOutputTypes.Intermediate) { LogHelper.title(`${this.skillFriendlyName} skill (on data)`)
LogHelper.title(`${this.skillFriendlyName} skill`) LogHelper.info(data.toString())
LogHelper.info(data.toString())
const speech = obj.output.speech.toString() const speech = obj.output.speech.toString()
if (!this.isMuted) { if (!this.isMuted) {
this.talk(speech) this.talk(speech)
}
this.speeches.push(speech)
} else {
this.skillOutput = data.toString()
} }
this.speeches.push(speech)
this.skillOutput = data.toString()
return Promise.resolve(null) return Promise.resolve(null)
} else { } else {
@ -388,7 +381,7 @@ export default class Brain {
// Catch the end of the skill execution // Catch the end of the skill execution
this.skillProcess?.stdout.on('end', () => { this.skillProcess?.stdout.on('end', () => {
LogHelper.title(`${this.skillFriendlyName} skill`) LogHelper.title(`${this.skillFriendlyName} skill (on end)`)
LogHelper.info(this.skillOutput) LogHelper.info(this.skillOutput)
let skillResult: SkillResult | undefined = undefined let skillResult: SkillResult | undefined = undefined
@ -398,36 +391,26 @@ export default class Brain {
try { try {
skillResult = JSON.parse(this.skillOutput) skillResult = JSON.parse(this.skillOutput)
if (skillResult?.output.speech) { // Synchronize the downloaded content if enabled
skillResult.output.speech = if (
skillResult.output.speech.toString() skillResult &&
if (!this.isMuted) { skillResult.output.options['synchronization'] &&
this.talk(skillResult.output.speech, true) skillResult.output.options['synchronization'].enabled &&
} skillResult.output.options['synchronization'].enabled === true
speeches.push(skillResult.output.speech) ) {
const sync = new Synchronizer(
this,
nluResult.classification,
skillResult.output.options['synchronization']
)
// Synchronize the downloaded content if enabled // When the synchronization is finished
if ( sync.synchronize((speech: string) => {
skillResult.output.type === SkillOutputTypes.End && if (!this.isMuted) {
skillResult.output.options['synchronization'] && this.talk(speech)
skillResult.output.options['synchronization'].enabled && }
skillResult.output.options['synchronization'].enabled === speeches.push(speech)
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)
})
}
} }
} catch (e) { } catch (e) {
LogHelper.title(`${this.skillFriendlyName} skill`) LogHelper.title(`${this.skillFriendlyName} skill`)

View File

@ -28,7 +28,6 @@ export interface SkillResult {
entities: NEREntity[] entities: NEREntity[]
slots: NLUSlots slots: NLUSlots
output: { output: {
type: SkillOutputTypes
codes: string[] codes: string[]
speech: string speech: string
core: SkillCoreData | undefined core: SkillCoreData | undefined
@ -41,21 +40,13 @@ export enum SkillBridges {
Python = 'python', Python = 'python',
NodeJS = 'nodejs' NodeJS = 'nodejs'
} }
export enum SkillOutputTypes {
Intermediate = 'inter',
End = 'end'
}
export enum SkillActionTypes { export enum SkillActionTypes {
Logic = 'logic', Logic = 'logic',
Dialog = 'dialog' Dialog = 'dialog'
} }
export interface IntentObject { export interface ActionParams {
id: string
lang: ShortLanguageCode lang: ShortLanguageCode
domain: NLPDomain
skill: NLPSkill
action: NLPAction
utterance: NLPUtterance utterance: NLPUtterance
current_entities: NEREntity[] current_entities: NEREntity[]
entities: NEREntity[] entities: NEREntity[]
@ -64,6 +55,13 @@ export interface IntentObject {
slots: { [key: string]: NLUSlot['value'] | undefined } slots: { [key: string]: NLUSlot['value'] | undefined }
} }
export interface IntentObject extends ActionParams {
id: string
domain: NLPDomain
skill: NLPSkill
action: NLPAction
}
export interface BrainProcessResult extends NLUResult { export interface BrainProcessResult extends NLUResult {
speeches: string[] speeches: string[]
executionTime: number executionTime: number

View File

@ -8,7 +8,7 @@ enum OSNames {
Linux = 'Linux', Linux = 'Linux',
Unknown = 'Unknown' Unknown = 'Unknown'
} }
enum BinaryFolderNames { export enum BinaryFolderNames {
Linux64Bit = 'linux-x86_64', // Linux 64-bit (Intel) Linux64Bit = 'linux-x86_64', // Linux 64-bit (Intel)
LinuxARM64 = 'linux-aarch64', // Linux 64-bit (ARM) LinuxARM64 = 'linux-aarch64', // Linux 64-bit (ARM)
MacOS64Bit = 'macosx-x86_64', // Apple 64-bit (Intel) MacOS64Bit = 'macosx-x86_64', // Apple 64-bit (Intel)

View File

View 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%"]
}
}

View File

View 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"
}
}

View 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}`
}
})
}
}
}

View File

@ -0,0 +1,6 @@
{
"configurations": {
"options": {},
"credentials": {}
}
}

View File

View File

9
skills/tsconfig.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": "@tsconfig/node16-strictest/tsconfig.json",
"compilerOptions": {
"paths": {
"@bridge/*": ["../bridges/nodejs/src/*"],
"@sdk/*": ["../bridges/nodejs/src/sdk/*"]
}
}
}

View File

@ -6,12 +6,15 @@
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@@/*": ["./*"], "@@/*": ["./*"],
"@/*": ["./server/src/*"] "@/*": ["./server/src/*"],
"@bridge/*": ["./bridges/nodejs/src/*"],
"@sdk/*": ["./bridges/nodejs/src/sdk/*"]
}, },
"ignoreDeprecations": "5.0", "ignoreDeprecations": "5.0",
"allowJs": true, "allowJs": true,
"checkJs": false, "checkJs": false,
"resolveJsonModule": true "resolveJsonModule": true,
"declaration": true
}, },
"ts-node": { "ts-node": {
"swc": true, "swc": true,
@ -20,5 +23,5 @@
}, },
"files": ["server/src/global.d.ts"], "files": ["server/src/global.d.ts"],
"include": ["server/src/**/*"], "include": ["server/src/**/*"],
"exclude": ["node_modules", "server/dist", "bridges/python", "tcp_server"] "exclude": ["node_modules", "server/dist", "bridges", "tcp_server"]
} }