jan/web/hooks/useCortex.ts
NamH 101268f6f3
feat: integrating cortex (#3001)
* feat: integrating cortex

* Temporary prevent crash

Signed-off-by: James <namnh0122@gmail.com>

* fix yarn lint

Signed-off-by: James <namnh0122@gmail.com>

* refactor: remove core node module - fs - extensions and so on (#3151)

* add migration script for threads, messages and models

Signed-off-by: James <namnh0122@gmail.com>

* remove freq_penalty and presence_penalty if model not supported

Signed-off-by: James <namnh0122@gmail.com>

* add back models in my models

Signed-off-by: James <namnh0122@gmail.com>

* fix api-url for setup API key popup

Signed-off-by: James <namnh0122@gmail.com>

* fix using model name for dropdown model

Signed-off-by: James <namnh0122@gmail.com>

* fix can't click to hotkey

Signed-off-by: James <namnh0122@gmail.com>

* fix: disable some UIs

Signed-off-by: James <namnh0122@gmail.com>

* fix build

Signed-off-by: James <namnh0122@gmail.com>

* reduce calling HF api

Signed-off-by: James <namnh0122@gmail.com>

* some ui update

Signed-off-by: James <namnh0122@gmail.com>

* feat: modal migration UI  (#3153)

* feat: handle popup migration

* chore: update loader

* chore: integrate script migration

* chore: cleanup import

* chore: moving out spinner loader

* chore: update check thread message success migrate

* chore: add handle script into retry button

* remove warning from joi

Signed-off-by: James <namnh0122@gmail.com>

* chore: fix duplicate children

* fix: path after migrating model

Signed-off-by: James <namnh0122@gmail.com>

* chore: apply mutation for config

* chore: prevent calling too many create assistant api

Signed-off-by: James <namnh0122@gmail.com>

* using cortexso

Signed-off-by: James <namnh0122@gmail.com>

* update download api

Signed-off-by: James <namnh0122@gmail.com>

* fix use on slider item

Signed-off-by: James <namnh0122@gmail.com>

* fix: ui no download model or simple onboarding (#3166)

* fix download huggingface model match with slider item

Signed-off-by: James <namnh0122@gmail.com>

* update owner_logo to logo and author

Signed-off-by: James <namnh0122@gmail.com>

* update new cortexso

Signed-off-by: James <namnh0122@gmail.com>

* Add install python step for macos

* add engine table

Signed-off-by: James <namnh0122@gmail.com>

* fix local icons

Signed-off-by: James <namnh0122@gmail.com>

* feat: add search feature for model hub

Signed-off-by: James <namnh0122@gmail.com>

* fix misalign switch

Signed-off-by: James <namnh0122@gmail.com>

* fix: delete thread not focus on other thread

Signed-off-by: James <namnh0122@gmail.com>

* add get model from hugging face

Signed-off-by: James <namnh0122@gmail.com>

* fix download from hugging face

Signed-off-by: James <namnh0122@gmail.com>

* small update

Signed-off-by: James <namnh0122@gmail.com>

* update

Signed-off-by: James <namnh0122@gmail.com>

* fix system monitor rounded only on the left

Signed-off-by: James <namnh0122@gmail.com>

* chore: update ui new hub screen (#3174)

* chore: update ui new hub screen

* chore: update layout centerpanel thread and hub screen

* chore: update detail model by group

* update cortexso 0.1.13

Signed-off-by: James <namnh0122@gmail.com>

* chore: add file size

Signed-off-by: James <namnh0122@gmail.com>

* chore: put engine to experimental feature

Signed-off-by: James <namnh0122@gmail.com>

* chore: open cortex folder

Signed-off-by: James <namnh0122@gmail.com>

* chore: add back user avatar

Signed-off-by: James <namnh0122@gmail.com>

* chore: minor UI hub (#3182)

* chore: add back right click thread list and update 3 dots are overlapping with the text

* chore: update position dropdown list my models

* chore: make on-device tab showing 6 items instead of 4

* chore: update style description modals detail model

* chore: update isGeneration loader and author name on modal

* feat: integrate cortex single executable

Signed-off-by: James <namnh0122@gmail.com>

* fix build

Signed-off-by: James <namnh0122@gmail.com>

* chore: added blank state

* chore: update ui component blank state

* bump cortex binary version

* fix: logic show modal migration (#3165)

* fix: logic show modal migration

* chore: fixed logic

* chore: read contain format gguf local models

* chore: change return hasLocalModel

* chore: intiial skipmigration state

* chore: filter embedding model

* fix: delete top thread not focus on any other thread

* chore: added UI no result component search models group (#3188)

* fix: remote model should show all when user config that engine

Signed-off-by: James <namnh0122@gmail.com>

* chore: set state thread and models migration using getOnInit (#3189)

* chore: set state thread and models migration using getOnInit

* chore: add state as dependecies hooks

* chore: system monitor panel show engine model (#3192)

* fix: remove config api, replace with engine

Signed-off-by: James <namnh0122@gmail.com>

* update

Signed-off-by: James <namnh0122@gmail.com>

* update reactquery

Signed-off-by: James <namnh0122@gmail.com>

* bump cortex 0.4.35

* feat: add waiting for cortex popup

Signed-off-by: James <namnh0122@gmail.com>

* chore: add loader detail model popup (#3195)

* chore: model start loader (#3197)

* chore: added model loader when user starting chat without model active

* chore: update copies loader

* fix: select min file size if recommended quant does not exist

Signed-off-by: James <namnh0122@gmail.com>

* chore: temporary hide gpu config

* fix: tensorrt not shown

Signed-off-by: James <namnh0122@gmail.com>

* fix lint

Signed-off-by: James <namnh0122@gmail.com>

* fix tests

Signed-off-by: James <namnh0122@gmail.com>

* fix e2e tests (wip)

Signed-off-by: James <namnh0122@gmail.com>

* update

Signed-off-by: James <namnh0122@gmail.com>

* fix: adding element and correct test to adapt new UI

* fix: temp skip unstable part

* fix: only show models which can be supported

Signed-off-by: James <namnh0122@gmail.com>

* Update version.txt

* update send message

Signed-off-by: James <namnh0122@gmail.com>

* fix: not allow user send message when is generating

Signed-off-by: James <namnh0122@gmail.com>

* chore: temp skip Playwright test due to env issue

* chore: temp skip Playwright test due to env issue

* update

Signed-off-by: James <namnh0122@gmail.com>

* chore: minor-ui-feedback (#3202)

---------

Signed-off-by: James <namnh0122@gmail.com>
Co-authored-by: Louis <louis@jan.ai>
Co-authored-by: Faisal Amir <urmauur@gmail.com>
Co-authored-by: Hien To <tominhhien97@gmail.com>
Co-authored-by: Van Pham <64197333+Van-QA@users.noreply.github.com>
Co-authored-by: Van-QA <van@jan.ai>
2024-07-26 17:52:43 +07:00

402 lines
11 KiB
TypeScript

import 'cortexso-node/shims/web'
import { useCallback } from 'react'
import {
Assistant,
Model,
Message,
Thread,
ChatCompletionCreateParamsNonStreaming,
ChatCompletionCreateParamsStreaming,
AssistantCreateParams,
AssistantUpdateParams,
LlmEngine,
} from '@janhq/core'
import { Cortex } from 'cortexso-node'
import { useAtomValue } from 'jotai'
import { UpdateConfigMutationVariables } from './useEngineMutation'
import { MessageCreateMutationVariables } from './useMessageCreateMutation'
import { MessageDeleteMutationVariables } from './useMessageDeleteMutation'
import { MessageUpdateMutationVariables } from './useMessageUpdateMutation'
import { hostAtom } from '@/helpers/atoms/AppConfig.atom'
const EngineInitStatuses = [
'ready',
'not_initialized',
'missing_configuration',
'not_supported',
] as const
export type EngineInitStatus = (typeof EngineInitStatuses)[number]
export type EngineStatus = {
name: LlmEngine
description: string
version: string
productName: string
status: EngineInitStatus
}
const useCortex = () => {
const host = useAtomValue(hostAtom)
const cortex = new Cortex({
baseURL: host,
apiKey: '',
dangerouslyAllowBrowser: true,
})
// TODO: put in to cortexso-node?
const getEngineStatuses = useCallback(async (): Promise<EngineStatus[]> => {
const response = await fetch(`${host}/engines`, {
method: 'GET',
})
const data = await response.json()
const engineStatuses: EngineStatus[] = []
data.data.forEach((engineStatus: EngineStatus) => {
engineStatuses.push(engineStatus)
})
return engineStatuses
}, [host])
// TODO: put in to cortexso-node?
const getEngineStatus = useCallback(
async (engine: LlmEngine): Promise<EngineStatus | undefined> => {
try {
const response = await fetch(`${host}/engines/${engine}`, {
method: 'GET',
})
const data = (await response.json()) as EngineStatus
return data
} catch (err) {
console.error(err)
}
},
[host]
)
// TODO: put in to cortexso-node?
const initializeEngine = useCallback(
async (engine: LlmEngine) => {
try {
await fetch(`${host}/engines/${engine}/init/`, {
method: 'POST',
headers: {
accept: 'application/json',
},
})
} catch (err) {
console.error(err)
}
},
[host]
)
const fetchAssistants = useCallback(async () => {
const assistants: Assistant[] = []
const response = await cortex.beta.assistants.list()
response.data.forEach((assistant) => {
assistants.push(assistant)
})
return assistants
}, [cortex.beta.assistants])
const fetchThreads = useCallback(async () => {
const threads: Thread[] = []
for await (const thread of cortex.beta.threads.list()) {
// @ts-expect-error each thread must have associated assistants
const assistants = thread['assistants'] as Assistant[]
if (!assistants || assistants.length === 0) continue
// @ts-expect-error each thread must have a title, else default to 'New Thread'
const title: string = thread['title'] ?? 'New Thread'
threads.push({
...thread,
title: title,
assistants: assistants,
})
}
return threads
}, [cortex.beta.threads])
const fetchModels = useCallback(async () => {
const models: Model[] = []
for await (const model of cortex.models.list()) {
// @ts-expect-error model should not be empty
const modelId = model.model
if (!modelId || modelId.length === 0) {
console.debug('Model id is empty, skipping', model)
continue
}
models.push({
...model,
model: modelId,
// @ts-expect-error each model must have associated files
files: model['files'],
})
}
return models
}, [cortex.models])
const fetchMessages = useCallback(
async (threadId: string) => {
try {
const messages: Message[] = []
const response = await cortex.beta.threads.messages.list(threadId)
response.data.forEach((message) => {
messages.push(message)
})
return messages
} catch (error) {
return []
}
},
[cortex.beta.threads.messages]
)
const startModel = useCallback(
async (modelId: string, options?: Record<string, unknown>) => {
await cortex.models.start(modelId, options ?? {})
},
[cortex.models]
)
const stopModel = useCallback(
async (modelId: string, options?: Record<string, unknown>) => {
await cortex.models.stop(modelId, options ?? {})
},
[cortex.models]
)
const chatCompletionNonStreaming = useCallback(
async (
chatCompletionCreateParams: ChatCompletionCreateParamsNonStreaming,
options?: Record<string, unknown>
// @ts-expect-error incompatible types
) => cortex.chat.completions.create(chatCompletionCreateParams, options),
[cortex.chat.completions]
)
const chatCompletionStreaming = useCallback(
async (
chatCompletionCreateParams: ChatCompletionCreateParamsStreaming,
options?: Record<string, unknown>
// @ts-expect-error incompatible types
) => cortex.chat.completions.create(chatCompletionCreateParams, options),
[cortex.chat.completions]
)
const deleteModel = useCallback(
async (modelId: string) => {
await cortex.models.del(modelId)
},
[cortex.models]
)
const cleanThread = useCallback(
async (threadId: string) => cortex.beta.threads.clean(threadId),
[cortex.beta.threads]
)
const deleteThread = useCallback(
async (threadId: string) => {
await cortex.beta.threads.del(threadId)
},
[cortex.beta.threads]
)
const updateThread = useCallback(
async (thread: Thread) => {
const result = await cortex.beta.threads.update(thread.id, thread)
console.debug(
`Update thread ${thread.id}, result: ${JSON.stringify(result, null, 2)}`
)
},
[cortex.beta.threads]
)
const deleteMessage = useCallback(
async (params: MessageDeleteMutationVariables) => {
const { threadId, messageId } = params
return cortex.beta.threads.messages.del(threadId, messageId)
},
[cortex.beta.threads]
)
const createMessage = useCallback(
async (params: MessageCreateMutationVariables) => {
const { threadId, createMessageParams } = params
return cortex.beta.threads.messages.create(threadId, createMessageParams)
},
[cortex.beta.threads]
)
const updateMessage = useCallback(
async (params: MessageUpdateMutationVariables) => {
const { threadId, messageId, data } = params
return cortex.beta.threads.messages.update(threadId, messageId, data)
},
[cortex.beta.threads]
)
const createThread = useCallback(
async (assistant: Assistant) => {
const createThreadResponse = await cortex.beta.threads.create({
// @ts-expect-error customize so that each thread will have an assistant
assistants: [assistant],
})
const thread: Thread = {
...createThreadResponse,
// @ts-expect-error each thread will have a title, else default to 'New Thread'
title: createThreadResponse.title ?? 'New Thread',
assistants: [assistant],
}
return thread
},
[cortex.beta.threads]
)
const updateModel = useCallback(
async (modelId: string, options: Record<string, unknown>) => {
try {
return await fetch(`${host}/models/${modelId}`, {
method: 'PATCH',
headers: {
'accept': 'application/json',
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
},
body: JSON.stringify(options),
})
} catch (err) {
console.error(err)
}
},
[host]
)
// TODO: put this into cortexso-node
const downloadModel = useCallback(
async (modelId: string, fileName?: string, persistedModelId?: string) => {
try {
return await fetch(`${host}/models/${modelId}/pull`, {
method: 'POST',
headers: {
'accept': 'application/json',
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileName: fileName,
persistedModelId: persistedModelId,
}),
})
} catch (err) {
console.error(err)
}
},
[host]
)
const abortDownload = useCallback(
async (downloadId: string) => {
try {
return await fetch(`${host}/models/${downloadId}/pull`, {
method: 'DELETE',
headers: {
'accept': 'application/json',
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
},
})
} catch (err) {
console.error(err)
}
},
[host]
)
const createAssistant = useCallback(
async (createParams: AssistantCreateParams) =>
cortex.beta.assistants.create(createParams),
[cortex.beta.assistants]
)
const updateAssistant = useCallback(
async (assistantId: string, updateParams: AssistantUpdateParams) =>
cortex.beta.assistants.update(assistantId, updateParams),
[cortex.beta.assistants]
)
// TODO: add this to cortex-node
const registerEngineConfig = useCallback(
async (variables: UpdateConfigMutationVariables) => {
try {
const { engine, config } = variables
await fetch(`${host}/engines/${engine}`, {
method: 'PATCH',
headers: {
'accept': 'application/json',
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
},
body: JSON.stringify(config),
})
} catch (err) {
console.error(err)
}
},
[host]
)
// add this to cortex-node?
const createModel = useCallback(
(model: Model) =>
fetch(`${host}/models`, {
method: 'POST',
headers: {
'accept': 'application/json',
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
},
body: JSON.stringify(model),
}),
[host]
)
return {
fetchAssistants,
fetchThreads,
fetchModels,
fetchMessages,
startModel,
stopModel,
chatCompletionStreaming,
deleteModel,
deleteThread,
deleteMessage,
cleanThread,
updateThread,
createMessage,
updateMessage,
createThread,
downloadModel,
abortDownload,
createAssistant,
updateAssistant,
updateModel,
chatCompletionNonStreaming,
registerEngineConfig,
createModel,
getEngineStatus,
initializeEngine,
getEngineStatuses,
}
}
export default useCortex