Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Nitro-Tensorrt-LLM Extension #2280

Merged
merged 40 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
43957bf
feat: tensorrt-llm-extension
louis-menlo Mar 8, 2024
a63b45d
fix: loading
louis-menlo Mar 12, 2024
bf44dcd
feat: add download tensorrt llm runner
Mar 11, 2024
ef3bd2f
feat: update to rollupjs instead of webpack for monitoring extension
Mar 12, 2024
c53c977
feat: move update nvidia info to monitor extension
Mar 12, 2024
ecb7d8e
allow download tensorrt
Mar 12, 2024
11b9d9b
update
Mar 12, 2024
a2ffeb6
allow download tensor rt based on gpu setting
Mar 12, 2024
825f2b7
update downloaded models
Mar 12, 2024
79f37f7
feat: add extension compatibility
louis-menlo Mar 12, 2024
d07bd6d
dynamic tensor rt engines
Mar 12, 2024
fe7e4a0
update models
Mar 12, 2024
72bf1ca
chore: remove ts-ignore
louis-menlo Mar 12, 2024
8ff5960
feat: getting installation state from extension
Mar 13, 2024
40bf24f
chore: adding type for decompress
Mar 13, 2024
46bf6c3
feat: update according Louis's comment
Mar 13, 2024
f06e9fe
feat: add progress for installing extension
Mar 13, 2024
de7f639
chore: remove args from extension installation
louis-menlo Mar 13, 2024
1c9f973
fix: model download does not work properly
louis-menlo Mar 13, 2024
d1516c3
fix: do not allow user to stop tensorrtllm inference
louis-menlo Mar 13, 2024
5e7cfec
fix: extension installed style
louis-menlo Mar 13, 2024
45d8741
fix: download tensorrt does not update state
Mar 13, 2024
82f958c
chore: replace int4 by fl16
louis-menlo Mar 13, 2024
8d39f53
feat: modal for installing extension
Mar 13, 2024
957288e
fix: start download immediately after press install
Mar 13, 2024
214988f
fix: error switching between engines
louis-menlo Mar 13, 2024
823472e
feat: rename inference provider to ai engine and refactor to core
louis-menlo Mar 13, 2024
89b7068
Merge pull request #2317 from janhq/feat/download-tensorrtllm-runner
louis-menlo Mar 13, 2024
f71c9de
fix: missing ulid
louis-menlo Mar 13, 2024
d689648
fix: core bundler
louis-menlo Mar 13, 2024
e22a23d
feat: add cancel extension installing
Mar 13, 2024
4f4eae9
remove mocking for mac
Mar 13, 2024
05c6ed3
fix: show models only when extension is ready
louis-menlo Mar 13, 2024
6313ad3
add tensorrt badge for model
Mar 13, 2024
bf0a170
fix: copy
louis-menlo Mar 13, 2024
e94f603
fix: add compatible check (#2342)
namchuai Mar 14, 2024
fb10643
fix: vulkan support
louis-menlo Mar 14, 2024
9a3a30f
fix: installation button padding
louis-menlo Mar 14, 2024
a49bdfc
fix: empty script
louis-menlo Mar 14, 2024
54b86e4
fix: remove hard code string
louis-menlo Mar 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ package-lock.json
core/lib/**

# Nitro binary files
extensions/inference-nitro-extension/bin/*/nitro
extensions/inference-nitro-extension/bin/*/*.metal
extensions/inference-nitro-extension/bin/*/*.exe
extensions/inference-nitro-extension/bin/*/*.dll
extensions/inference-nitro-extension/bin/*/*.exp
extensions/inference-nitro-extension/bin/*/*.lib
extensions/inference-nitro-extension/bin/saved-*
extensions/inference-nitro-extension/bin/*.tar.gz
extensions/inference-nitro-extension/bin/vulkaninfoSDK.exe
extensions/inference-nitro-extension/bin/vulkaninfo
extensions/*-extension/bin/*/nitro
extensions/*-extension/bin/*/*.metal
extensions/*-extension/bin/*/*.exe
extensions/*-extension/bin/*/*.dll
extensions/*-extension/bin/*/*.exp
extensions/*-extension/bin/*/*.lib
extensions/*-extension/bin/saved-*
extensions/*-extension/bin/*.tar.gz
extensions/*-extension/bin/vulkaninfoSDK.exe
extensions/*-extension/bin/vulkaninfo


# Turborepo
Expand Down
12 changes: 8 additions & 4 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@
"start": "rollup -c rollup.config.ts -w"
},
"devDependencies": {
"jest": "^29.7.0",
"@types/jest": "^29.5.12",
"@types/node": "^12.0.2",
"eslint-plugin-jest": "^27.9.0",
"eslint": "8.57.0",
"eslint-plugin-jest": "^27.9.0",
"jest": "^29.7.0",
"rimraf": "^3.0.2",
"rollup": "^2.38.5",
"rollup-plugin-commonjs": "^9.1.8",
"rollup-plugin-json": "^3.1.0",
Expand All @@ -58,7 +59,10 @@
"rollup-plugin-typescript2": "^0.36.0",
"ts-jest": "^29.1.2",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"rimraf": "^3.0.2"
"typescript": "^5.3.3"
},
"dependencies": {
"rxjs": "^7.8.1",
"ulid": "^2.3.0"
}
}
2 changes: 1 addition & 1 deletion core/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export default [
// Allow json resolution
json(),
// Compile TypeScript files
typescript({ useTsconfigDeclarationDir: true }),
typescript({ useTsconfigDeclarationDir: true, exclude: ['src/*.ts', 'src/extensions/**'] }),
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
commonjs(),
// Allow node_modules resolution, so you can use 'external' to control
Expand Down
3 changes: 3 additions & 0 deletions core/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export enum AppRoute {
stopServer = 'stopServer',
log = 'log',
logServer = 'logServer',
systemInformations = 'systemInformations',
showToast = 'showToast',
}

export enum AppEvent {
Expand All @@ -56,6 +58,7 @@ export enum DownloadEvent {
onFileDownloadUpdate = 'onFileDownloadUpdate',
onFileDownloadError = 'onFileDownloadError',
onFileDownloadSuccess = 'onFileDownloadSuccess',
onFileUnzipSuccess = 'onFileUnzipSuccess',
}

export enum LocalImportModelEvent {
Expand Down
36 changes: 25 additions & 11 deletions core/src/core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FileStat } from './types'
import { DownloadRequest, FileStat, NetworkConfig } from './types'

/**
* Execute a extension module function in main process
Expand All @@ -17,18 +17,16 @@ const executeOnMain: (extension: string, method: string, ...args: any[]) => Prom

/**
* Downloads a file from a URL and saves it to the local file system.
* @param {string} url - The URL of the file to download.
* @param {string} fileName - The name to use for the downloaded file.
* @param {object} network - Optional object to specify proxy/whether to ignore SSL certificates.
*
* @param {DownloadRequest} downloadRequest - The request to download the file.
* @param {NetworkConfig} network - Optional object to specify proxy/whether to ignore SSL certificates.
*
* @returns {Promise<any>} A promise that resolves when the file is downloaded.
*/
const downloadFile: (
url: string,
fileName: string,
network?: { proxy?: string; ignoreSSL?: boolean }
) => Promise<any> = (url, fileName, network) => {
return global.core?.api?.downloadFile(url, fileName, network)
}
const downloadFile: (downloadRequest: DownloadRequest, network?: NetworkConfig) => Promise<any> = (
downloadRequest,
network
) => global.core?.api?.downloadFile(downloadRequest, network)

/**
* Aborts the download of a specific file.
Expand Down Expand Up @@ -108,6 +106,20 @@ const log: (message: string, fileName?: string) => void = (message, fileName) =>
const isSubdirectory: (from: string, to: string) => Promise<boolean> = (from: string, to: string) =>
global.core.api?.isSubdirectory(from, to)

/**
* Get system information
* @returns {Promise<any>} - A promise that resolves with the system information.
*/
const systemInformations: () => Promise<any> = () => global.core.api?.systemInformations()

/**
* Show toast message from browser processes.
* @param title
* @param message
* @returns
*/
const showToast: (title: string, message: string) => void = (title, message) =>
global.core.api?.showToast(title, message)
/**
* Register extension point function type definition
*/
Expand All @@ -134,5 +146,7 @@ export {
log,
isSubdirectory,
getUserHomePath,
systemInformations,
showToast,
FileStat,
}
44 changes: 44 additions & 0 deletions core/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ export enum ExtensionTypeEnum {
export interface ExtensionType {
type(): ExtensionTypeEnum | undefined
}

export interface Compatibility {
platform: string[]
version: string
}

const ALL_INSTALLATION_STATE = [
'NotRequired', // not required.
'Installed', // require and installed. Good to go.
'NotInstalled', // require to be installed.
'Corrupted', // require but corrupted. Need to redownload.
] as const

export type InstallationStateTuple = typeof ALL_INSTALLATION_STATE
export type InstallationState = InstallationStateTuple[number]

/**
* Represents a base extension.
* This class should be extended by any class that represents an extension.
Expand All @@ -33,4 +49,32 @@ export abstract class BaseExtension implements ExtensionType {
* Any cleanup logic for the extension should be put here.
*/
abstract onUnload(): void

/**
* The compatibility of the extension.
* This is used to check if the extension is compatible with the current environment.
* @property {Array} platform
*/
compatibility(): Compatibility | undefined {
return undefined
}

/**
* Determine if the prerequisites for the extension are installed.
*
* @returns {boolean} true if the prerequisites are installed, false otherwise.
*/
async installationState(): Promise<InstallationState> {
return 'NotRequired'
}

/**
* Install the prerequisites for the extension.
*
* @returns {Promise<void>}
*/
// @ts-ignore
async install(...args): Promise<void> {
return
}
}
60 changes: 60 additions & 0 deletions core/src/extensions/ai-engines/AIEngine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { getJanDataFolderPath, joinPath } from '../../core'
import { events } from '../../events'
import { BaseExtension } from '../../extension'
import { fs } from '../../fs'
import { Model, ModelEvent } from '../../types'

/**
* Base AIEngine
* Applicable to all AI Engines
*/
export abstract class AIEngine extends BaseExtension {
// The inference engine
abstract provider: string
// The model folder
modelFolder: string = 'models'

abstract models(): Promise<Model[]>

/**
* On extension load, subscribe to events.
*/
onLoad() {
this.prePopulateModels()
}

/**
* Pre-populate models to App Data Folder
*/
prePopulateModels(): Promise<void> {
return this.models().then((models) => {
const prePoluateOperations = models.map((model) =>
getJanDataFolderPath()
.then((janDataFolder) =>
// Attempt to create the model folder
joinPath([janDataFolder, this.modelFolder, model.id]).then((path) =>
fs
.mkdirSync(path)
.catch()
.then(() => path)
)
)
.then((path) => joinPath([path, 'model.json']))
.then((path) => {
// Do not overwite existing model.json
return fs.existsSync(path).then((exist: any) => {
if (!exist) return fs.writeFileSync(path, JSON.stringify(model, null, 2))
})
})
.catch((e: Error) => {
console.error('Error', e)
})
)
Promise.all(prePoluateOperations).then(() =>
// Emit event to update models
// So the UI can update the models list
events.emit(ModelEvent.OnModelsUpdate, {})
)
})
}
}
63 changes: 63 additions & 0 deletions core/src/extensions/ai-engines/LocalOAIEngine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { executeOnMain, getJanDataFolderPath, joinPath } from '../../core'
import { events } from '../../events'
import { Model, ModelEvent } from '../../types'
import { OAIEngine } from './OAIEngine'

/**
* Base OAI Local Inference Provider
* Added the implementation of loading and unloading model (applicable to local inference providers)
*/
export abstract class LocalOAIEngine extends OAIEngine {
// The inference engine
loadModelFunctionName: string = 'loadModel'
unloadModelFunctionName: string = 'unloadModel'
isRunning: boolean = false

/**
* On extension load, subscribe to events.
*/
onLoad() {
super.onLoad()
// These events are applicable to local inference providers
events.on(ModelEvent.OnModelInit, (model: Model) => this.onModelInit(model))
events.on(ModelEvent.OnModelStop, (model: Model) => this.onModelStop(model))
}

/**
* Load the model.
*/
async onModelInit(model: Model) {
if (model.engine.toString() !== this.provider) return

const modelFolder = await joinPath([await getJanDataFolderPath(), this.modelFolder, model.id])

const res = await executeOnMain(this.nodeModule, this.loadModelFunctionName, {
modelFolder,
model,
})

if (res?.error) {
events.emit(ModelEvent.OnModelFail, {
...model,
error: res.error,
})
return
} else {
this.loadedModel = model
events.emit(ModelEvent.OnModelReady, model)
this.isRunning = true
}
}
/**
* Stops the model.
*/
onModelStop(model: Model) {
if (model.engine?.toString() !== this.provider) return

this.isRunning = false

executeOnMain(this.nodeModule, this.unloadModelFunctionName).then(() => {
events.emit(ModelEvent.OnModelStopped, {})
})
}
}
Loading
Loading