From f5c19bccc5b05f26e34a2e753d2943e34a29d3fa Mon Sep 17 00:00:00 2001 From: Linh Tran Date: Fri, 1 Dec 2023 23:02:35 +0700 Subject: [PATCH] Switch to fastify + add model CRUD impl --- server/data/models/.gitkeep | 0 server/data/threads/.gitkeep | 0 server/main.ts | 29 ++++--------- server/package.json | 1 + server/v1/assistants/index.ts | 11 +++-- server/v1/chat/index.ts | 14 ++++-- server/v1/index.ts | 47 +++++++++++++------- server/v1/models/downloadModel.ts | 24 +++++++++-- server/v1/models/index.ts | 71 +++++++++++++++++++++++++------ server/v1/models/modelOp.ts | 11 +++++ server/v1/threads/index.ts | 9 ++-- 11 files changed, 154 insertions(+), 63 deletions(-) create mode 100644 server/data/models/.gitkeep create mode 100644 server/data/threads/.gitkeep create mode 100644 server/v1/models/modelOp.ts diff --git a/server/data/models/.gitkeep b/server/data/models/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/server/data/threads/.gitkeep b/server/data/threads/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/server/main.ts b/server/main.ts index 582af5c61..5466a27d8 100644 --- a/server/main.ts +++ b/server/main.ts @@ -1,28 +1,17 @@ -import express from 'express' -import bodyParser from 'body-parser' -import fs from 'fs' +import fastify from 'fastify' + import v1API from './v1' - const JAN_API_PORT = 1337; - -const server = express() -server.use(bodyParser.urlencoded()) -server.use(bodyParser.json()) +const server = fastify() const USER_ROOT_DIR = '.data' -server.use("/v1", v1API) +server.register(v1API, {prefix: "/api/v1"}) -// server.post("fs", (req, res) => { -// let op = req.body.op; -// switch(op){ -// case 'readFile': -// fs.readFile(req.body.path, ()=>{}) -// case 'writeFile': -// fs.writeFile(req.body.path, Buffer.from(req.body.data, "base64"), ()=>{}) -// } -// }) -server.listen(JAN_API_PORT, () => { - console.log(`JAN API listening at: http://localhost:${JAN_API_PORT}`); +server.listen({ + port: JAN_API_PORT, + host: "0.0.0.0" +}).then(()=>{ + console.log(`JAN API listening at: http://0.0.0.0:${JAN_API_PORT}`); }) diff --git a/server/package.json b/server/package.json index 08c19ee31..405f2bf5b 100644 --- a/server/package.json +++ b/server/package.json @@ -75,6 +75,7 @@ "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", "eslint-plugin-react": "^7.33.2", + "fastify": "^4.24.3", "nodemon": "^3.0.1", "run-script-os": "^1.1.6" }, diff --git a/server/v1/assistants/index.ts b/server/v1/assistants/index.ts index 7b51b801f..c722195d0 100644 --- a/server/v1/assistants/index.ts +++ b/server/v1/assistants/index.ts @@ -1,5 +1,8 @@ -import { Request, Response } from 'express' +import { FastifyInstance, FastifyPluginAsync, FastifyPluginOptions } from 'fastify' -export default function route(req: Request, res: Response){ - -} \ No newline at end of file +const router: FastifyPluginAsync = async (app: FastifyInstance, opts: FastifyPluginOptions) => { + //TODO: Add controllers for assistants here + // app.get("/", controller) + // app.post("/", controller) +} +export default router; \ No newline at end of file diff --git a/server/v1/chat/index.ts b/server/v1/chat/index.ts index 7b51b801f..cb5fbf120 100644 --- a/server/v1/chat/index.ts +++ b/server/v1/chat/index.ts @@ -1,5 +1,11 @@ -import { Request, Response } from 'express' +import { FastifyInstance, FastifyPluginAsync, FastifyPluginOptions } from 'fastify' -export default function route(req: Request, res: Response){ - -} \ No newline at end of file +const router: FastifyPluginAsync = async (app: FastifyInstance, opts: FastifyPluginOptions) => { + //TODO: Add controllers for here + // app.get("/", controller) + + app.post("/", (req, res) => { + req.body + }) +} +export default router; \ No newline at end of file diff --git a/server/v1/index.ts b/server/v1/index.ts index 7528b917d..8a153cbaf 100644 --- a/server/v1/index.ts +++ b/server/v1/index.ts @@ -1,20 +1,37 @@ -import { Request, Response } from 'express' - import assistantsAPI from './assistants' import chatCompletionAPI from './chat' import modelsAPI from './models' import threadsAPI from './threads' -export default function route(req: Request, res: Response){ - console.log(req.path.split("/")[1]) - switch (req.path.split("/")[1]){ - case 'assistants': - assistantsAPI(req, res) - case 'chat': - chatCompletionAPI(req, res) - case 'models': - modelsAPI(req, res) - case 'threads': - threadsAPI(req, res) - } -} \ No newline at end of file +import { FastifyInstance, FastifyPluginAsync } from 'fastify' + +const router: FastifyPluginAsync = async (app: FastifyInstance, opts) => { + app.register( + assistantsAPI, + { + prefix: "/assisstants" + } + ) + + app.register( + chatCompletionAPI, + { + prefix: "/chat/completion" + } + ) + + app.register( + modelsAPI, + { + prefix: "/models" + } + ) + + app.register( + threadsAPI, + { + prefix: "/threads" + } + ) +} +export default router; \ No newline at end of file diff --git a/server/v1/models/downloadModel.ts b/server/v1/models/downloadModel.ts index 89db0cfce..d564a2207 100644 --- a/server/v1/models/downloadModel.ts +++ b/server/v1/models/downloadModel.ts @@ -1,5 +1,23 @@ -import { Request, Response } from 'express' +import { RouteHandlerMethod, FastifyRequest, FastifyReply } from 'fastify' +import { MODEL_FOLDER_PATH } from "./index" +import fs from 'fs/promises' -export default function controller(req: Request, res: Response){ +const controller: RouteHandlerMethod = async (req: FastifyRequest, res: FastifyReply) => { + //TODO: download models impl + //Mirror logic from JanModelExtension.downloadModel? + let model = req.body.model; -} \ No newline at end of file + // Fetching logic + // const directoryPath = join(MODEL_FOLDER_PATH, model.id) + // await fs.mkdir(directoryPath) + + // const path = join(directoryPath, model.id) + // downloadFile(model.source_url, path) + // TODO: Different model downloader from different model vendor + + res.status(200).send({ + status: "Ok" + }) +} + +export default controller; \ No newline at end of file diff --git a/server/v1/models/index.ts b/server/v1/models/index.ts index 091e46283..22c551300 100644 --- a/server/v1/models/index.ts +++ b/server/v1/models/index.ts @@ -1,18 +1,61 @@ -import { Request, Response } from 'express' -import downloadModelController from './downloadModel' +export const MODEL_FOLDER_PATH = "./data/models" +export const _modelMetadataFileName = 'model.json' -function getModelController(req: Request, res: Response){ - -} +import fs from 'fs/promises' +import { Model } from '@janhq/core' +import { join } from 'path' -export default function route(req: Request, res: Response){ - switch(req.method){ - case 'get': - getModelController(req, res) - break; - case 'post': - downloadModelController(req, res) - break; +// map string => model object +let modelIndex = new Map(); +async function buildModelIndex(){ + let modelIds = await fs.readdir(MODEL_FOLDER_PATH); + // TODO: read modelFolders to get model info, mirror JanModelExtension? + try{ + for(let modelId in modelIds){ + let path = join(MODEL_FOLDER_PATH, modelId) + let fileData = await fs.readFile(join(path, _modelMetadataFileName)) + modelIndex.set(modelId, JSON.parse(fileData.toString("utf-8")) as Model) + } } -} \ No newline at end of file + catch(err){ + console.error("build model index failed. ", err); + } +} +buildModelIndex() + +import { FastifyInstance, FastifyPluginAsync, FastifyPluginOptions } from 'fastify' +import downloadModelController from './downloadModel' +import { startModel, stopModel } from './modelOp' + +const router: FastifyPluginAsync = async (app: FastifyInstance, opts: FastifyPluginOptions) => { + //TODO: Add controllers declaration here + + ///////////// CRUD //////////////// + // Model listing + app.get("/", async (req, res) => { + res.status(200).send( + modelIndex.values() + ) + }) + + // Retrieve model info + app.get("/:id", (req, res) => { + res.status(200).send( + modelIndex.get(req.params.id) + ) + }) + + // Delete model + app.delete("/:id", (req, res) => { + modelIndex.delete(req.params) + + // TODO: delete on disk + }) + + ///////////// Other ops //////////////// + app.post("/", downloadModelController) + app.put("/start", startModel) + app.put("/stop", stopModel) +} +export default router; \ No newline at end of file diff --git a/server/v1/models/modelOp.ts b/server/v1/models/modelOp.ts new file mode 100644 index 000000000..f2c7ffe75 --- /dev/null +++ b/server/v1/models/modelOp.ts @@ -0,0 +1,11 @@ +import {FastifyRequest, FastifyReply} from 'fastify' + +export async function startModel(req: FastifyRequest, res: FastifyReply): Promise { + + +} + +export async function stopModel(req: FastifyRequest, res: FastifyReply): Promise { + + +} \ No newline at end of file diff --git a/server/v1/threads/index.ts b/server/v1/threads/index.ts index 7b51b801f..e63f9e8d8 100644 --- a/server/v1/threads/index.ts +++ b/server/v1/threads/index.ts @@ -1,5 +1,8 @@ -import { Request, Response } from 'express' +import { FastifyInstance, FastifyPluginAsync, FastifyPluginOptions } from 'fastify' -export default function route(req: Request, res: Response){ +const router: FastifyPluginAsync = async (app: FastifyInstance, opts: FastifyPluginOptions) => { + //TODO: Add controllers declaration here -} \ No newline at end of file + // app.get() +} +export default router; \ No newline at end of file