mirror of
https://github.com/leon-ai/leon.git
synced 2024-09-11 18:27:21 +03:00
refactor(server): HTTP server and structure (wip)
This commit is contained in:
parent
b3b10e326f
commit
69e90c3b5f
@ -4,7 +4,7 @@
|
||||
# Language currently used
|
||||
LEON_LANG=en-US
|
||||
|
||||
# Server
|
||||
# HttpServer
|
||||
LEON_HOST=http://localhost
|
||||
LEON_PORT=1337
|
||||
|
||||
|
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@ -51,7 +51,7 @@ Choose the setup method you want to go for.
|
||||
|
||||
### Single-Click
|
||||
|
||||
Gitpod will automatically setup an environment and run an instance for you.
|
||||
Gitpod will automatically set up an environment and run an instance for you.
|
||||
|
||||
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/leon-ai/leon)
|
||||
|
||||
@ -149,7 +149,7 @@ By sponsoring the project you make the project sustainable and faster to develop
|
||||
|
||||
The focus is not only limited to the activity you see on GitHub but also a lot of thinking about the direction of the project. Which is naturally related to the overall design, architecture, vision, learning process and so on...
|
||||
|
||||
## Contributing to the Python Bridge or TCP Server
|
||||
## Contributing to the Python Bridge or TCP HttpServer
|
||||
|
||||
Leon makes use of two binaries, the Python bridge and the TCP server. These binaries are compiled from Python sources.
|
||||
|
||||
|
@ -68,8 +68,8 @@ If you want to, Leon can communicate with you by being **offline to protect your
|
||||
> - Skills
|
||||
> - The web app
|
||||
> - The hotword node
|
||||
> - The TCP server (for inter-process communication between Leon and third-party processes such as spaCy)
|
||||
> - The Python bridge (the connector between Python core and skills)
|
||||
> - The TCP server (for inter-process communication between Leon and third-party nodes such as spaCy)
|
||||
> - The Python bridge (the connector between the core and skills made with Python)
|
||||
|
||||
### What is Leon able to do?
|
||||
|
||||
@ -81,7 +81,7 @@ Sounds good to you? Then let's get started!
|
||||
|
||||
## ☁️ Try with a Single-Click
|
||||
|
||||
Gitpod will automatically setup an environment and run an instance for you.
|
||||
Gitpod will automatically set up an environment and run an instance for you.
|
||||
|
||||
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/leon-ai/leon)
|
||||
|
||||
|
@ -453,7 +453,7 @@ dotenv.config()
|
||||
if (!fs.existsSync(flitePath)) {
|
||||
report.can_offline_tts.v = false
|
||||
LogHelper.warning(
|
||||
`Cannot find ${flitePath}. You can setup the offline TTS by running: "npm run setup:offline-tts"\n`
|
||||
`Cannot find ${flitePath}. You can set up the offline TTS by running: "npm run setup:offline-tts"\n`
|
||||
)
|
||||
} else {
|
||||
LogHelper.success(`Found Flite at ${flitePath}\n`)
|
||||
|
@ -9,6 +9,6 @@ import setupHotword from './setup-hotword'
|
||||
try {
|
||||
await setupHotword()
|
||||
} catch (e) {
|
||||
LogHelper.error(`Failed to setup offline hotword: ${e}`)
|
||||
LogHelper.error(`Failed to set up offline hotword: ${e}`)
|
||||
}
|
||||
})()
|
||||
|
@ -9,6 +9,6 @@ import setupStt from './setup-stt'
|
||||
try {
|
||||
await setupStt()
|
||||
} catch (e) {
|
||||
LogHelper.error(`Failed to setup offline STT: ${e}`)
|
||||
LogHelper.error(`Failed to set up offline STT: ${e}`)
|
||||
}
|
||||
})()
|
||||
|
@ -9,6 +9,6 @@ import setupTts from './setup-tts'
|
||||
try {
|
||||
await setupTts()
|
||||
} catch (e) {
|
||||
LogHelper.error(`Failed to setup offline TTS: ${e}`)
|
||||
LogHelper.error(`Failed to set up offline TTS: ${e}`)
|
||||
}
|
||||
})()
|
||||
|
@ -6,7 +6,7 @@ import { LogHelper } from '@/helpers/log-helper'
|
||||
import { OSHelper } from '@/helpers/os-helper'
|
||||
|
||||
/**
|
||||
* Setup offline speech-to-text
|
||||
* Set up offline speech-to-text
|
||||
*/
|
||||
export default () =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
|
@ -6,7 +6,7 @@ import { LogHelper } from '@/helpers/log-helper'
|
||||
import { OSHelper } from '@/helpers/os-helper'
|
||||
|
||||
/**
|
||||
* Setup offline text-to-speech
|
||||
* Set up offline text-to-speech
|
||||
*/
|
||||
export default () =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
|
@ -4,7 +4,7 @@ import path from 'node:path'
|
||||
import { LogHelper } from '@/helpers/log-helper'
|
||||
|
||||
/**
|
||||
* Setup Leon's core configuration
|
||||
* Set up Leon's core configuration
|
||||
*/
|
||||
export default () =>
|
||||
new Promise((resolve) => {
|
||||
|
@ -7,7 +7,7 @@ import { LogHelper } from '@/helpers/log-helper'
|
||||
import { SkillDomainHelper } from '@/helpers/skill-domain-helper'
|
||||
|
||||
/**
|
||||
* Setup skills configuration
|
||||
* Set up skills configuration
|
||||
*/
|
||||
export default () =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
|
139
server/src/core/http-server/http-server.ts
Normal file
139
server/src/core/http-server/http-server.ts
Normal file
@ -0,0 +1,139 @@
|
||||
import { join } from 'node:path'
|
||||
|
||||
import Fastify from 'fastify'
|
||||
import fastifyStatic from '@fastify/static'
|
||||
import { Server as SocketIOServer } from 'socket.io'
|
||||
|
||||
import { version } from '@@/package.json'
|
||||
import { LEON_NODE_ENV, HAS_LOGGER, IS_DEVELOPMENT_ENV } from '@/constants'
|
||||
import { LogHelper } from '@/helpers/log-helper'
|
||||
import { DateHelper } from '@/helpers/date-helper'
|
||||
import corsMidd from '@/core/http-server/plugins/cors'
|
||||
import otherMidd from '@/core/http-server/plugins/other'
|
||||
import infoPlugin from '@/core/http-server/api/info'
|
||||
import downloadsPlugin from '@/core/http-server/api/downloads'
|
||||
// import keyMidd from '@/core/http-server/plugins/key'
|
||||
import server from '@/core/http-server/old-server'
|
||||
|
||||
const API_VERSION = 'v1'
|
||||
|
||||
export default class HTTPServer {
|
||||
private static instance: HTTPServer
|
||||
|
||||
private fastify = Fastify()
|
||||
private httpServer = this.fastify.server
|
||||
|
||||
constructor(private readonly host: string, private readonly port: number) {
|
||||
if (!HTTPServer.instance) {
|
||||
LogHelper.title('HTTP Server')
|
||||
LogHelper.success('New instance')
|
||||
|
||||
HTTPServer.instance = this
|
||||
}
|
||||
|
||||
this.host = host
|
||||
this.port = port
|
||||
}
|
||||
|
||||
/**
|
||||
* Server entry point
|
||||
*/
|
||||
public async init(): Promise<void> {
|
||||
this.fastify.addHook('onRequest', corsMidd)
|
||||
this.fastify.addHook('preValidation', otherMidd)
|
||||
|
||||
LogHelper.title('Initialization')
|
||||
LogHelper.info(`The current env is ${LEON_NODE_ENV}`)
|
||||
LogHelper.info(`The current version is ${version}`)
|
||||
|
||||
LogHelper.info(`The current time zone is ${DateHelper.getTimeZone()}`)
|
||||
|
||||
const sLogger = !HAS_LOGGER ? 'disabled' : 'enabled'
|
||||
LogHelper.info(`Collaborative logger ${sLogger}`)
|
||||
|
||||
// await addProvider('1')
|
||||
|
||||
await this.bootstrap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap API
|
||||
*/
|
||||
private async bootstrap(): Promise<void> {
|
||||
// Render the web app
|
||||
this.fastify.register(fastifyStatic, {
|
||||
root: join(process.cwd(), 'app/dist'),
|
||||
prefix: '/'
|
||||
})
|
||||
this.fastify.get('/', (_request, reply) => {
|
||||
reply.sendFile('index.html')
|
||||
})
|
||||
|
||||
this.fastify.register(infoPlugin, { apiVersion: API_VERSION })
|
||||
this.fastify.register(downloadsPlugin, { apiVersion: API_VERSION })
|
||||
|
||||
// TODO: HTTP API
|
||||
/*if (HAS_OVER_HTTP) {
|
||||
server.fastify.register((instance, opts, next) => {
|
||||
instance.addHook('preHandler', keyMidd)
|
||||
|
||||
instance.post('/api/query', async (request, reply) => {
|
||||
const { utterance } = request.body
|
||||
|
||||
try {
|
||||
const data = await mainProvider.nlu.process(utterance, { mute: true })
|
||||
|
||||
reply.send({
|
||||
...data,
|
||||
success: true
|
||||
})
|
||||
} catch (e) {
|
||||
reply.statusCode = 500
|
||||
reply.send({
|
||||
message: e.message,
|
||||
success: false
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
server.generateSkillsRoutes(instance)
|
||||
|
||||
next()
|
||||
})
|
||||
}*/
|
||||
|
||||
try {
|
||||
await this.listen()
|
||||
} catch (e) {
|
||||
// TODO: remove ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
LogHelper.error(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch server
|
||||
*/
|
||||
private async listen(): Promise<void> {
|
||||
const io = IS_DEVELOPMENT_ENV
|
||||
? new SocketIOServer(this.httpServer, {
|
||||
cors: { origin: `${this.host}:3000` }
|
||||
})
|
||||
: new SocketIOServer(this.httpServer)
|
||||
|
||||
// TODO: instanciate new socket server
|
||||
io.on('connection', server.handleOnConnection)
|
||||
|
||||
this.fastify.listen(
|
||||
{
|
||||
port: this.port,
|
||||
host: '0.0.0.0'
|
||||
},
|
||||
() => {
|
||||
LogHelper.title('Initialization')
|
||||
LogHelper.success(`Server is available at ${this.host}:${this.port}`)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { TCP_SERVER_HOST, TCP_SERVER_PORT } from '@/constants'
|
||||
import { HOST, PORT, TCP_SERVER_HOST, TCP_SERVER_PORT } from '@/constants'
|
||||
import TCPClient from '@/core/tcp-client'
|
||||
import HTTPServer from '@/core/http-server/http-server'
|
||||
|
||||
/**
|
||||
* Register core singletons
|
||||
@ -9,3 +10,5 @@ export const TCP_CLIENT = new TCPClient(
|
||||
String(TCP_SERVER_HOST),
|
||||
TCP_SERVER_PORT
|
||||
)
|
||||
|
||||
export const HTTP_SERVER = new HTTPServer(String(HOST), PORT)
|
||||
|
@ -5,9 +5,8 @@ import {
|
||||
LANG as LEON_LANG,
|
||||
TCP_SERVER_BIN_PATH
|
||||
} from '@/constants'
|
||||
import { TCP_CLIENT } from '@/core'
|
||||
import { TCP_CLIENT, HTTP_SERVER } from '@/core'
|
||||
import { LangHelper } from '@/helpers/lang-helper'
|
||||
import server from '@/core/http-server/server'
|
||||
;(async (): Promise<void> => {
|
||||
process.title = 'leon'
|
||||
|
||||
@ -23,6 +22,18 @@ import server from '@/core/http-server/server'
|
||||
// Connect the TCP client to the TCP server
|
||||
TCP_CLIENT.connect()
|
||||
|
||||
// Start the core server
|
||||
await server.init()
|
||||
// Start the HTTP server
|
||||
// await server.init()
|
||||
await HTTP_SERVER.init()
|
||||
|
||||
// TODO
|
||||
// Register HTTP API endpoints
|
||||
// await HTTP_API.register()
|
||||
|
||||
// TODO
|
||||
// const httpServer = HTTP_SERVER.httpServer
|
||||
|
||||
// TODO
|
||||
// Start the socket server
|
||||
// SOCKET_SERVER.init(httpServer)
|
||||
})()
|
||||
|
@ -28,7 +28,7 @@ parser.init = (args) => {
|
||||
|
||||
if (!fs.existsSync(args.model)) {
|
||||
LogHelper.error(
|
||||
`Cannot find ${args.model}. You can setup the offline STT by running: "npm run setup:offline-stt"`
|
||||
`Cannot find ${args.model}. You can set up the offline STT by running: "npm run setup:offline-stt"`
|
||||
)
|
||||
|
||||
return false
|
||||
|
@ -35,7 +35,7 @@ synthesizer.init = (lang) => {
|
||||
/* istanbul ignore if */
|
||||
if (!fs.existsSync(flitePath)) {
|
||||
LogHelper.error(
|
||||
`Cannot find ${flitePath} You can setup the offline TTS by running: "npm run setup:offline-tts"`
|
||||
`Cannot find ${flitePath} You can set up the offline TTS by running: "npm run setup:offline-tts"`
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
@ -121,4 +121,4 @@ def raise_connection_error(response):
|
||||
elif response == "KO - ELEM LIST IS EMPTY" or response == "WARN - NO QUESTION":
|
||||
raise AkiNoQuestions("\"Akinator.step\" reached 79. No more questions")
|
||||
else:
|
||||
raise AkiConnectionFailure("An unknown error has occured. Server response: {}".format(response))
|
||||
raise AkiConnectionFailure("An unknown error has occured. HttpServer response: {}".format(response))
|
||||
|
@ -168,7 +168,7 @@ def get_all():
|
||||
'GraphQL',
|
||||
'Graphviz (DOT)',
|
||||
'Groovy',
|
||||
'Groovy Server Pages',
|
||||
'Groovy HttpServer Pages',
|
||||
'Hack',
|
||||
'Haml',
|
||||
'Handlebars',
|
||||
@ -205,7 +205,7 @@ def get_all():
|
||||
'Jasmin',
|
||||
'Java',
|
||||
'Java Properties',
|
||||
'Java Server Pages',
|
||||
'Java HttpServer Pages',
|
||||
'JavaScript',
|
||||
'JFlex',
|
||||
'Jison',
|
||||
|
@ -323,7 +323,7 @@ class ServersRetrievalError(SpeedtestHTTPError):
|
||||
|
||||
|
||||
class InvalidServerIDType(SpeedtestException):
|
||||
"""Server ID used for filtering was not an integer"""
|
||||
"""HttpServer ID used for filtering was not an integer"""
|
||||
|
||||
|
||||
class NoMatchedServers(SpeedtestException):
|
||||
@ -335,7 +335,7 @@ class SpeedtestMiniConnectFailure(SpeedtestException):
|
||||
|
||||
|
||||
class InvalidSpeedtestMiniServer(SpeedtestException):
|
||||
"""Server provided as a speedtest mini server does not actually appear
|
||||
"""HttpServer provided as a speedtest mini server does not actually appear
|
||||
to be a speedtest mini server
|
||||
"""
|
||||
|
||||
@ -1042,7 +1042,7 @@ class SpeedtestResults(object):
|
||||
def csv_header(delimiter=','):
|
||||
"""Return CSV Headers"""
|
||||
|
||||
row = ['Server ID', 'Sponsor', 'Server Name', 'Timestamp', 'Distance',
|
||||
row = ['HttpServer ID', 'Sponsor', 'HttpServer Name', 'Timestamp', 'Distance',
|
||||
'Ping', 'Download', 'Upload', 'Share', 'IP Address']
|
||||
out = StringIO()
|
||||
writer = csv.writer(out, delimiter=delimiter, lineterminator='')
|
||||
@ -1394,7 +1394,7 @@ class Speedtest(object):
|
||||
extension = [ext]
|
||||
break
|
||||
if not urlparts or not extension:
|
||||
raise InvalidSpeedtestMiniServer('Invalid Speedtest Mini Server: '
|
||||
raise InvalidSpeedtestMiniServer('Invalid Speedtest Mini HttpServer: '
|
||||
'%s' % server)
|
||||
|
||||
self.servers = [{
|
||||
@ -1501,7 +1501,7 @@ class Speedtest(object):
|
||||
self.results.server = best
|
||||
|
||||
self._best.update(best)
|
||||
printer('Best Server:\n%r' % best, debug=True)
|
||||
printer('Best HttpServer:\n%r' % best, debug=True)
|
||||
return best
|
||||
|
||||
def download(self, callback=do_nothing, threads=None):
|
||||
|
@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
|
||||
import server from '@/core/http-server/server'
|
||||
import server from '@/core/http-server/http-server'
|
||||
|
||||
const urlPrefix = `${process.env.LEON_HOST}:${process.env.LEON_PORT}/api`
|
||||
const queryUrl = `${urlPrefix}/query`
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { EventEmitter } from 'node:events'
|
||||
|
||||
import server from '@/core/http-server/server'
|
||||
import server from '@/core/http-server/http-server'
|
||||
|
||||
describe('server', () => {
|
||||
describe('init()', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user