1
1
mirror of https://github.com/leon-ai/leon.git synced 2024-11-23 09:43:19 +03:00

feat(server): move to ESM

This commit is contained in:
louistiti 2024-11-20 21:47:05 +08:00
parent 529620b83a
commit ea8ba0f1fd
28 changed files with 226 additions and 176 deletions

View File

@ -1,80 +0,0 @@
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"prettier"
],
"settings": {
"import/resolver": {
"typescript": true,
"node": true
}
},
"parser": "@typescript-eslint/parser",
"env": {
"node": true,
"browser": true
},
"parserOptions": {
"ecmaVersion": "latest"
},
"plugins": ["@typescript-eslint", "unicorn", "import"],
"ignorePatterns": "*.spec.js",
"rules": {
"@typescript-eslint/no-non-null-assertion": ["off"],
"no-async-promise-executor": ["off"],
"no-underscore-dangle": ["error", { "allowAfterThis": true }],
"prefer-destructuring": ["off"],
"comma-dangle": ["error", "never"],
"semi": ["error", "never"],
"object-curly-spacing": ["error", "always"],
"unicorn/prefer-node-protocol": "error",
"@typescript-eslint/member-delimiter-style": [
"error",
{
"multiline": {
"delimiter": "none",
"requireLast": true
},
"singleline": {
"delimiter": "comma",
"requireLast": false
}
}
],
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/consistent-type-definitions": "error",
"import/no-named-as-default": "off",
"import/no-named-as-default-member": "off",
"import/order": [
"error",
{
"groups": [
"builtin",
"external",
"internal",
"parent",
"sibling",
"index"
],
"newlines-between": "always"
}
]
},
"overrides": [
{
"files": ["skills/**/*.ts"],
"rules": {
"import/order": "off"
}
},
{
"files": ["*.ts"],
"rules": {
"@typescript-eslint/explicit-function-return-type": "error"
}
}
]
}

View File

@ -1,6 +1,7 @@
import { createElement } from 'react'
import { createRoot } from 'react-dom/client'
import axios from 'axios'
// eslint-disable-next-line no-redeclare
import { WidgetWrapper, Flexbox, Loader, Text } from '@leon-ai/aurora'
import renderAuroraComponent from './render-aurora-component'

View File

@ -441,7 +441,6 @@ export default class Client {
const voiceContainer = document.querySelector('#voice-container')
if (voiceContainer) {
voiceContainer.style.animation = 'none'
voiceContainer.offsetHeight
voiceContainer.style.animation = null
}

View File

@ -9,13 +9,11 @@ export default function renderAuroraComponent(
supportedEvents
) {
if (component) {
// eslint-disable-next-line import/namespace
let reactComponent = auroraComponents[component.component]
/**
* Find custom component if a former component is not found
*/
if (!reactComponent) {
// eslint-disable-next-line import/namespace
reactComponent = customAuroraComponents[component.component]
}

View File

@ -1,23 +1,21 @@
{
"langs": {
"en-US": {
"short": "en",
"min_confidence": 0.5,
"fallbacks": [],
"action_loop_stop_words": ["stop", "break", "exit"]
},
"fr-FR": {
"short": "fr",
"min_confidence": 0.5,
"fallbacks": [
{
"words": ["merci"],
"domain": "leon",
"skill": "welcome",
"action": "run"
}
],
"action_loop_stop_words": ["stop", "break", "exit"]
}
"en-US": {
"short": "en",
"min_confidence": 0.5,
"fallbacks": [],
"action_loop_stop_words": ["stop", "break", "exit"]
},
"fr-FR": {
"short": "fr",
"min_confidence": 0.5,
"fallbacks": [
{
"words": ["merci"],
"domain": "leon",
"skill": "welcome",
"action": "run"
}
],
"action_loop_stop_words": ["stop", "break", "exit"]
}
}

116
eslint.config.mjs Normal file
View File

@ -0,0 +1,116 @@
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import { fixupConfigRules, fixupPluginRules } from '@eslint/compat'
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import stylisticTs from '@stylistic/eslint-plugin-ts'
import unicorn from 'eslint-plugin-unicorn'
import _import from 'eslint-plugin-import'
import globals from 'globals'
import tsParser from '@typescript-eslint/parser'
import js from '@eslint/js'
import { FlatCompat } from '@eslint/eslintrc'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
})
export default [
{
ignores: ['**/*.spec.js']
},
...fixupConfigRules(
compat.extends(
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'prettier'
)
),
{
plugins: {
'@typescript-eslint': fixupPluginRules(typescriptEslint),
'@stylistic/ts': stylisticTs,
unicorn,
import: fixupPluginRules(_import)
},
languageOptions: {
globals: {
...globals.node,
...globals.browser
},
parser: tsParser,
ecmaVersion: 'latest',
sourceType: 'commonjs'
},
settings: {
'import/resolver': {
typescript: true,
node: true
}
},
rules: {
'@typescript-eslint/no-non-null-assertion': ['off'],
'no-async-promise-executor': ['off'],
'no-underscore-dangle': [
'error',
{
allowAfterThis: true
}
],
'prefer-destructuring': ['off'],
'comma-dangle': ['error', 'never'],
semi: ['error', 'never'],
'object-curly-spacing': ['error', 'always'],
'unicorn/prefer-node-protocol': 'error',
'@stylistic/ts/member-delimiter-style': [
'error',
{
multiline: {
delimiter: 'none',
requireLast: true
},
singleline: {
delimiter: 'comma',
requireLast: false
}
}
],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/consistent-type-definitions': 'error',
'import/no-named-as-default': 'off',
'import/no-named-as-default-member': 'off',
'import/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index'
],
'newlines-between': 'always'
}
]
}
},
{
files: ['skills/**/*.ts'],
rules: {
'import/order': 'off'
}
},
{
files: ['**/*.ts'],
rules: {
'@typescript-eslint/explicit-function-return-type': 'error'
}
}
]

View File

@ -113,7 +113,11 @@
"tree-kill": "1.2.2"
},
"devDependencies": {
"@eslint/compat": "1.2.3",
"@eslint/eslintrc": "3.2.0",
"@eslint/js": "9.15.0",
"@nlpjs/utils": "4.24.1",
"@stylistic/eslint-plugin-ts": "2.11.0",
"@tsconfig/node16": "16.1.1",
"@tsconfig/strictest": "2.0.2",
"@types/archiver": "6.0.1",
@ -124,17 +128,18 @@
"@types/node-wav": "0.0.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@typescript-eslint/eslint-plugin": "6.11.0",
"@typescript-eslint/parser": "6.11.0",
"@typescript-eslint/eslint-plugin": "8.15.0",
"@typescript-eslint/parser": "8.15.0",
"@vercel/ncc": "0.38.1",
"@vitejs/plugin-react": "4.1.1",
"cli-spinner": "0.2.10",
"eslint": "8.53.0",
"eslint": "9.15.0",
"eslint-config-prettier": "9.0.0",
"eslint-import-resolver-typescript": "3.6.1",
"eslint-plugin-import": "npm:eslint-plugin-i@2.29.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-unicorn": "49.0.0",
"git-changelog": "2.0.0",
"globals": "15.12.0",
"husky": "8.0.3",
"inquirer": "12.1.0",
"jest": "27.4.7",

View File

@ -101,7 +101,7 @@ BUILD_TARGETS.set('tcp-server', {
const installPatchelfCommand = 'sudo apt install patchelf'
LogHelper.error(
`The "patchelf" utility is not installed. Please run the following command: "${installPatchelfCommand}" or install it via a packages manager supported by your Linux distribution such as DNF, YUM, etc. Then try again`
`The "patchelf" utility is not installed. Please run the following command: "${installPatchelfCommand}" or install it via a packages manager supported by your Linux distribution such as DNF, YUM, etc. Then try again. Details: ${e}`
)
process.exit(1)
}

View File

@ -1,5 +1,6 @@
import fs from 'node:fs'
import path from 'node:path'
// eslint-disable-next-line no-redeclare
import crypto from 'node:crypto'
import dotenv from 'dotenv'

View File

@ -3,7 +3,7 @@ import path from 'node:path'
import dotenv from 'dotenv'
import { langs } from '@@/core/langs.json'
import { LANG_CONFIGS } from '@/constants.js'
import { LogHelper } from '@/helpers/log-helper'
import { SkillDomainHelper } from '@/helpers/skill-domain-helper'
@ -30,7 +30,7 @@ export default () =>
'core',
'skills-endpoints.json'
)
const lang = langs[process.env.LEON_HTTP_API_LANG].short
const lang = LANG_CONFIGS[process.env.LEON_HTTP_API_LANG].short
try {
const skillDomains = await SkillDomainHelper.getSkillDomains()

View File

@ -20,10 +20,10 @@ const globs = [
const src = globs.join(' ')
async function prettier() {
await command('prettier --write . --ignore-path .gitignore', {
await command('prettier --write . --ignore-pattern .gitignore', {
shell: true
})
await command(`prettier --check ${src} --ignore-path .gitignore`, {
await command(`prettier --check ${src} --ignore-pattern .gitignore`, {
shell: true,
stdio: 'inherit'
})
@ -39,7 +39,7 @@ async function prettier() {
try {
await Promise.all([
prettier(),
command(`eslint ${src} --ignore-path .gitignore`, {
command(`eslint ${src} --ignore-pattern .gitignore`, {
shell: true,
stdio: 'inherit'
})

View File

@ -369,7 +369,7 @@ SPACY_MODELS.set('fr', {
LogHelper.success('All spaCy models are already installed')
} catch (e) {
LogHelper.info('Not all spaCy models are installed')
LogHelper.info(`Not all spaCy models are installed. Details: ${e}`)
await installSpacyModels()
}
}

View File

@ -15,6 +15,10 @@ const TESTING_ENV = 'testing'
export const GITHUB_URL = 'https://github.com/leon-ai/leon'
export const API_VERSION = 'v1'
export const { default: LANG_CONFIGS } = await import('@@/core/langs.json', {
with: { type: 'json' }
})
/**
* Environments
*/

View File

@ -21,12 +21,12 @@ import type {
} from '@/core/brain/types'
import type { AnswerOutput } from '@sdk/types'
import { SkillActionTypes, SkillBridges } from '@/core/brain/types'
import { langs } from '@@/core/langs.json'
import {
HAS_TTS,
PYTHON_BRIDGE_BIN_PATH,
NODEJS_BRIDGE_BIN_PATH,
TMP_PATH
TMP_PATH,
LANG_CONFIGS
} from '@/constants'
import {
CONVERSATION_LOGGER,
@ -387,7 +387,7 @@ export default class Brain {
private shouldAskToRepeat(nluResult: NLUResult): boolean {
return (
nluResult.classification.confidence <
langs[LangHelper.getLongCode(this._lang)].min_confidence
LANG_CONFIGS[LangHelper.getLongCode(this._lang)].min_confidence
)
}
@ -481,7 +481,7 @@ export default class Brain {
}
} catch (e) {
LogHelper.title('Brain')
LogHelper.debug(`process.stdout: ${String(data)}`)
LogHelper.debug(`process.stdout: ${String(data)}. Details: ${e}`)
}
}

View File

@ -21,7 +21,7 @@ import { LLMProviders, LLMDuties } from '@/core/llm-manager/types'
import { LLM_PROVIDER as LLM_PROVIDER_NAME } from '@/constants'
import { StringHelper } from '@/helpers/string-helper'
interface ParaphraseLLMDutyParams extends LLMDutyParams {}
type ParaphraseLLMDutyParams = LLMDutyParams
export class ParaphraseLLMDuty extends LLMDuty {
private static instance: ParaphraseLLMDuty

View File

@ -78,7 +78,7 @@ export default class LLMProvider {
SERVER_CORE_PATH,
'llm-manager',
'llm-providers',
LLM_PROVIDERS_MAP[LLM_PROVIDER as LLMProviders]
`${LLM_PROVIDERS_MAP[LLM_PROVIDER as LLMProviders]}.js`
)
)
this.llmProvider = new provider()

View File

@ -181,6 +181,9 @@ export class ActionLoop {
return processedData
} catch (e) {
LogHelper.title('Action Loop')
LogHelper.warning(`Failed to execute action loop: ${e}`)
return null
}
}

View File

@ -86,7 +86,7 @@ export default class ModelLoader {
} catch (e) {
reject(
new Error(
'An error occurred while loading the global resolvers NLP model'
`An error occurred while loading the global resolvers NLP model. Details: ${e}`
)
)
}
@ -127,7 +127,7 @@ export default class ModelLoader {
} catch (e) {
reject(
new Error(
'An error occurred while loading the skills resolvers NLP model'
`An error occurred while loading the skills resolvers NLP model. Details: ${e}`
)
)
}
@ -174,7 +174,9 @@ export default class ModelLoader {
resolve()
} catch (e) {
reject(
new Error('An error occurred while loading the main NLP model')
new Error(
`An error occurred while loading the main NLP model. Details: ${e}`
)
)
}
}

View File

@ -12,8 +12,7 @@ import type {
NLPUtterance,
NLUResult
} from '@/core/nlp/types'
import { langs } from '@@/core/langs.json'
import { PYTHON_TCP_SERVER_BIN_PATH } from '@/constants'
import { LANG_CONFIGS, PYTHON_TCP_SERVER_BIN_PATH } from '@/constants'
import {
PYTHON_TCP_CLIENT,
BRAIN,
@ -353,6 +352,7 @@ export default class NLU {
if (Object.keys(this.conversation.activeContext.slots).length > 0) {
try {
return resolve(await SlotFilling.handle(utterance))
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return reject({})
}
@ -395,7 +395,7 @@ export default class NLU {
if (intent === 'None') {
const fallback = this.fallback(
langs[LangHelper.getLongCode(locale)].fallbacks
LANG_CONFIGS[LangHelper.getLongCode(locale)].fallbacks
)
if (!fallback) {
@ -458,6 +458,7 @@ export default class NLU {
) {
try {
return resolve(await SlotFilling.handle(utterance))
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return reject({})
}

View File

@ -103,8 +103,6 @@ export type NLUSlots = Record<string, NLUSlot>
* NER types
*/
/* eslint-disable @typescript-eslint/no-empty-interface */
export const BUILT_IN_ENTITY_TYPES = [
'number',
'ip',
@ -178,10 +176,10 @@ interface Entity<
* Built-in entity types
*/
export interface BuiltInEntity<
export type BuiltInEntity<
Type extends BuiltInEntityType,
Resolution extends Record<string, unknown>
> extends Entity<Type, Resolution> {}
> = Entity<Type, Resolution>
export type BuiltInNumberEntity = BuiltInEntity<
'number',
@ -360,10 +358,10 @@ export type BuiltInTemperatureEntity = BuiltInEntity<
* Custom entity types
*/
interface CustomEntity<
type CustomEntity<
Type extends CustomEntityType | SpacyEntityType,
Resolution extends Record<string, unknown> = { value: string }
> extends Entity<Type, Resolution, string> {}
> = Entity<Type, Resolution, string>
export interface CustomEnumEntity<
Type extends CustomEntityType | SpacyEntityType = 'enum',
@ -375,7 +373,7 @@ export interface CustomEnumEntity<
alias?: string
}
type GlobalEntity = CustomEnumEntity
export interface CustomRegexEntity extends CustomEntity<'regex'> {}
export type CustomRegexEntity = CustomEntity<'regex'>
interface CustomTrimEntity extends CustomEntity<'trim'> {
subtype:
| 'between'
@ -409,32 +407,30 @@ interface SpacyLocationCountryData {
currencycode: string
phone: string
}
export interface SpacyLocationCountryEntity
extends SpacyEntity<
'location:country',
{
value: string
data: SpacyLocationCountryData
export type SpacyLocationCountryEntity = SpacyEntity<
'location:country',
{
value: string
data: SpacyLocationCountryData
}
>
export type SpacyLocationCityEntity = SpacyEntity<
'location:city',
{
value: string
data: {
name: string
latitude: number
longitude: number
countrycode: string
country: SpacyLocationCountryData
population: number
timezone: string
}
> {}
export interface SpacyLocationCityEntity
extends SpacyEntity<
'location:city',
{
value: string
data: {
name: string
latitude: number
longitude: number
countrycode: string
country: SpacyLocationCountryData
population: number
timezone: string
}
}
> {}
export interface SpacyPersonEntity extends SpacyEntity<'person'> {}
export interface SpacyOrganizationEntity extends SpacyEntity<'organization'> {}
}
>
export type SpacyPersonEntity = SpacyEntity<'person'>
export type SpacyOrganizationEntity = SpacyEntity<'organization'>
/**
* Exported entity types

View File

@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/ban-types */
declare module '@ffprobe-installer/ffprobe' {
export const path: string
}
@ -19,7 +17,8 @@ interface LangAll {
}
declare module '@nlpjs/core-loader' {
export const containerBootstrap: Function
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const containerBootstrap: () => Promise<any>
}
declare module '@nlpjs/nlp' {
export const Nlp: Nlp

View File

@ -1,6 +1,6 @@
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc.js'
import timezone from 'dayjs/plugin/timezone.js'
import { TIME_ZONE } from '@/constants'
import { LogHelper } from '@/helpers/log-helper'
@ -39,7 +39,7 @@ export class DateHelper {
timeZone = TIME_ZONE
} catch (e) {
LogHelper.warning(
`The time zone "${TIME_ZONE}" is not valid. Falling back to "${timeZone}"`
`The time zone "${TIME_ZONE}" is not valid. Falling back to "${timeZone}". Details: ${e}`
)
}
}

View File

@ -1,5 +1,5 @@
import { langs } from '@@/core/langs.json'
import type { LongLanguageCode, ShortLanguageCode } from '@/types'
import { LANG_CONFIGS } from '@/constants'
export class LangHelper {
/**
@ -7,9 +7,9 @@ export class LangHelper {
* @example getShortCodes() // ["en", "fr"]
*/
public static getShortCodes(): ShortLanguageCode[] {
const longLanguages = Object.keys(langs) as LongLanguageCode[]
const longLanguages = Object.keys(LANG_CONFIGS) as LongLanguageCode[]
return longLanguages.map((lang) => langs[lang].short)
return longLanguages.map((lang) => LANG_CONFIGS[lang].short)
}
/**
@ -18,9 +18,9 @@ export class LangHelper {
* @example getLongCode('en') // en-US
*/
public static getLongCode(shortCode: ShortLanguageCode): LongLanguageCode {
for (const longLanguage in langs) {
for (const longLanguage in LANG_CONFIGS) {
const longLanguageType = longLanguage as LongLanguageCode
const lang = langs[longLanguageType]
const lang = LANG_CONFIGS[longLanguageType]
if (lang.short === shortCode) {
return longLanguageType
@ -36,7 +36,7 @@ export class LangHelper {
* @example getShortCode('en-US') // en
*/
public static getShortCode(longCode: LongLanguageCode): ShortLanguageCode {
return langs[longCode].short
return LANG_CONFIGS[longCode].short
}
/**
@ -45,6 +45,7 @@ export class LangHelper {
* @example getActionLoopStopWords('en-US') // ["stop", "break", "exit"]
*/
public static getActionLoopStopWords(shortCode: ShortLanguageCode): string[] {
return langs[LangHelper.getLongCode(shortCode)].action_loop_stop_words
return LANG_CONFIGS[LangHelper.getLongCode(shortCode)]
.action_loop_stop_words
}
}

View File

@ -13,6 +13,7 @@ export class NetworkHelper {
await axios.head(HUGGING_FACE_URL)
return true
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return false
}

View File

@ -50,7 +50,8 @@ export class SkillDomainHelper {
if ((await fs.promises.stat(domainPath)).isDirectory()) {
const skills: SkillDomain['skills'] = {}
const { name: domainName } = (await import(
path.join(domainPath, 'domain.json')
path.join(domainPath, 'domain.json'),
{ with: { type: 'json' } }
)) as DomainSchema
const skillFolders = await fs.promises.readdir(domainPath)
const domainPathParts = domainPath.split('/')

View File

@ -56,6 +56,7 @@ export class Telemetry {
})
return data
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
return {}
}

View File

@ -1,5 +1,3 @@
import type { langs } from '@@/core/langs.json'
/**
* Contain common/shared types that are universal across the project
* and cannot be placed in the respective core nodes
@ -15,7 +13,12 @@ import type { langs } from '@@/core/langs.json'
* @see https://www.iso.org/iso-3166-country-codes.html
*/
export type Languages = typeof langs
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { default: LANG_CONFIGS } = await import('@@/core/langs.json', {
with: { type: 'json' }
})
export type Languages = typeof LANG_CONFIGS
export type LongLanguageCode = keyof Languages
export type Language = Languages[LongLanguageCode]
export type ShortLanguageCode = Language['short']

View File

@ -5,7 +5,7 @@
"outDir": "./server/dist",
"baseUrl": ".",
"moduleResolution": "Node",
"module": "CommonJS",
"module": "ESNext",
"jsx": "react",
"paths": {
"@@/*": ["./*"],