Faisal Amir 2a0601f75a
feat: remote engine management (#4364)
* feat: remote engine management

* chore: fix linter issue

* chore: remove unused imports

* fix: populate engines, models and legacy settings (#4403)

* fix: populate engines, models and legacy settings

* chore: legacy logics update configured remote engine

* fix: check exist path before reading

* fix: engines and models persist - race condition

* chore: update issue state

* test: update test cases

* chore: bring back Cortex extension settings

* chore: setup button gear / plus based apikey

* chore: fix remote engine from welcome screen

* chore: resolve linter issue

* chore: support request headers template

* chore: update engines using header_template instead of api_key_template

* chore: update models on changes

* fix: anthropic response template

* chore: fix welcome screen and debounce update value input

* chore: update engines list on changes

* chore: update engines list on change

* chore: update desc form add modal remote engines

* chore: bump cortex version to latest RC

* chore: fix linter

* fix: transform payload of Anthropic and OpenAI

* fix: typo

* fix: openrouter model id for auto routing

* chore: remove remote engine URL setting

* chore: add cohere engine and model support

* fix: should not clean on app launch - models list display issue

* fix: local engine check logic

* chore: bump app version to latest release 0.5.13

* test: fix failed tests

---------

Co-authored-by: Louis <louis@jan.ai>
2025-01-14 17:29:56 +07:00

199 lines
4.4 KiB
TypeScript

import PQueue from 'p-queue'
import ky from 'ky'
import { extractModelLoadParams, Model } from '@janhq/core'
import { extractInferenceParams } from '@janhq/core'
/**
* cortex.cpp Model APIs interface
*/
interface ICortexAPI {
getModel(model: string): Promise<Model>
getModels(): Promise<Model[]>
pullModel(model: string, id?: string, name?: string): Promise<void>
importModel(
path: string,
modelPath: string,
name?: string,
option?: string
): Promise<void>
deleteModel(model: string): Promise<void>
updateModel(model: object): Promise<void>
cancelModelPull(model: string): Promise<void>
configs(body: { [key: string]: any }): Promise<void>
}
type ModelList = {
data: any[]
}
export class CortexAPI implements ICortexAPI {
queue = new PQueue({ concurrency: 1 })
constructor() {
this.queue.add(() => this.healthz())
}
/**
* Fetches a model detail from cortex.cpp
* @param model
* @returns
*/
getModel(model: string): Promise<any> {
return this.queue.add(() =>
ky
.get(`${API_URL}/v1/models/${model}`)
.json()
.then((e) => this.transformModel(e))
)
}
/**
* Fetches models list from cortex.cpp
* @param model
* @returns
*/
getModels(): Promise<Model[]> {
return this.queue
.add(() => ky.get(`${API_URL}/v1/models?limit=-1`).json<ModelList>())
.then((e) =>
typeof e === 'object' ? e.data.map((e) => this.transformModel(e)) : []
)
}
/**
* Pulls a model from HuggingFace via cortex.cpp
* @param model
* @returns
*/
pullModel(model: string, id?: string, name?: string): Promise<void> {
return this.queue.add(() =>
ky
.post(`${API_URL}/v1/models/pull`, { json: { model, id, name } })
.json()
.catch(async (e) => {
throw (await e.response?.json()) ?? e
})
.then()
)
}
/**
* Imports a model from a local path via cortex.cpp
* @param model
* @returns
*/
importModel(
model: string,
modelPath: string,
name?: string,
option?: string
): Promise<void> {
return this.queue.add(() =>
ky
.post(`${API_URL}/v1/models/import`, {
json: { model, modelPath, name, option },
})
.json()
.catch((e) => console.debug(e)) // Ignore error
.then()
)
}
/**
* Deletes a model from cortex.cpp
* @param model
* @returns
*/
deleteModel(model: string): Promise<void> {
return this.queue.add(() =>
ky.delete(`${API_URL}/v1/models/${model}`).json().then()
)
}
/**
* Update a model in cortex.cpp
* @param model
* @returns
*/
updateModel(model: Partial<Model>): Promise<void> {
return this.queue.add(() =>
ky
.patch(`${API_URL}/v1/models/${model.id}`, { json: { ...model } })
.json()
.then()
)
}
/**
* Cancel model pull in cortex.cpp
* @param model
* @returns
*/
cancelModelPull(model: string): Promise<void> {
return this.queue.add(() =>
ky
.delete(`${API_URL}/v1/models/pull`, { json: { taskId: model } })
.json()
.then()
)
}
/**
* Check model status
* @param model
*/
async getModelStatus(model: string): Promise<boolean> {
return this.queue
.add(() => ky.get(`${API_URL}/v1/models/status/${model}`))
.then((e) => true)
.catch(() => false)
}
/**
* Do health check on cortex.cpp
* @returns
*/
healthz(): Promise<void> {
return ky
.get(`${API_URL}/healthz`, {
retry: {
limit: 20,
delay: () => 500,
methods: ['get'],
},
})
.then(() => {})
}
/**
* Configure model pull options
* @param body
*/
configs(body: { [key: string]: any }): Promise<void> {
return this.queue.add(() =>
ky.patch(`${API_URL}/v1/configs`, { json: body }).then(() => {})
)
}
/**
* TRansform model to the expected format (e.g. parameters, settings, metadata)
* @param model
* @returns
*/
private transformModel(model: any) {
model.parameters = {
...extractInferenceParams(model),
...model.parameters,
...model.inference_params,
}
model.settings = {
...extractModelLoadParams(model),
...model.settings,
}
model.metadata = model.metadata ?? {
tags: [],
size: model.size ?? model.metadata?.size ?? 0,
}
return model as Model
}
}