fix: empty model page not shown when delete all threads and models (#3343)
* fix: empty model page not shown when delete all threads and models * fix: blank state when delete jan data folder content (#3345) * test template name --------- Co-authored-by: Van Pham <64197333+Van-QA@users.noreply.github.com>
This commit is contained in:
parent
fdab8af057
commit
9e29fcd69e
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,5 +1,5 @@
|
||||
---
|
||||
name: Bug report
|
||||
name: _Bug report
|
||||
about: Create a report to help us improve Jan
|
||||
title: 'bug: [DESCRIPTION]'
|
||||
labels: 'type: bug'
|
||||
|
||||
@ -1,11 +1,21 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { Engine } from '@cortexso/cortex.js/resources'
|
||||
import {
|
||||
EngineStatus,
|
||||
LocalEngine,
|
||||
LocalEngines,
|
||||
Model,
|
||||
RemoteEngine,
|
||||
RemoteEngines,
|
||||
} from '@janhq/core'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
import useAssistantCreate, { janAssistant } from '@/hooks/useAssistantCreate'
|
||||
import useAssistantQuery from '@/hooks/useAssistantQuery'
|
||||
import useCortex from '@/hooks/useCortex'
|
||||
import useEngineQuery from '@/hooks/useEngineQuery'
|
||||
import { useLoadTheme } from '@/hooks/useLoadTheme'
|
||||
import useModelHub from '@/hooks/useModelHub'
|
||||
@ -13,17 +23,24 @@ import useModelQuery from '@/hooks/useModelQuery'
|
||||
import useThreadCreateMutation from '@/hooks/useThreadCreateMutation'
|
||||
import useThreadQuery from '@/hooks/useThreadQuery'
|
||||
|
||||
import { getSelectedModelAtom } from '@/helpers/atoms/Model.atom'
|
||||
import {
|
||||
getSelectedModelAtom,
|
||||
updateSelectedModelAtom,
|
||||
} from '@/helpers/atoms/Model.atom'
|
||||
import { threadsAtom } from '@/helpers/atoms/Thread.atom'
|
||||
|
||||
const DataLoader: React.FC = () => {
|
||||
const selectedModel = useAtomValue(getSelectedModelAtom)
|
||||
const setSelectedModel = useSetAtom(updateSelectedModelAtom)
|
||||
const allThreads = useAtomValue(threadsAtom)
|
||||
const { data: assistants } = useAssistantQuery()
|
||||
const { data: models } = useModelQuery()
|
||||
const { data: threads, isLoading: isFetchingThread } = useThreadQuery()
|
||||
const { data: engineData } = useEngineQuery()
|
||||
const { data: modelHubData } = useModelHub()
|
||||
const createThreadMutation = useThreadCreateMutation()
|
||||
const assistantCreateMutation = useAssistantCreate()
|
||||
const { createModel } = useCortex()
|
||||
|
||||
useEffect(() => {
|
||||
if (!assistants) return
|
||||
@ -34,19 +51,105 @@ const DataLoader: React.FC = () => {
|
||||
}
|
||||
}, [assistants, assistantCreateMutation])
|
||||
|
||||
const isAnyRemoteModelConfigured = useMemo(() => {
|
||||
if (!engineData) return false
|
||||
|
||||
let result = false
|
||||
for (const engine of engineData) {
|
||||
if (RemoteEngines.includes(engine.name as RemoteEngine)) {
|
||||
if (engine.status === EngineStatus.Ready) {
|
||||
result = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}, [engineData])
|
||||
|
||||
const isAnyModelReady = useMemo(() => {
|
||||
if (!models) return false
|
||||
return models.length > 0
|
||||
}, [models])
|
||||
|
||||
// automatically create new thread if thread list is empty
|
||||
useEffect(() => {
|
||||
if (isFetchingThread) return
|
||||
if (allThreads.length > 0) return
|
||||
if (!assistants || assistants.length === 0) return
|
||||
if (!models || models.length === 0) return
|
||||
if (allThreads.length === 0 && !createThreadMutation.isPending) {
|
||||
const model = selectedModel ?? models[0]
|
||||
const assistant = assistants[0]
|
||||
const shouldCreateNewThread = isAnyRemoteModelConfigured || isAnyModelReady
|
||||
|
||||
console.log('Create new thread because user have no thread')
|
||||
if (shouldCreateNewThread && !createThreadMutation.isPending) {
|
||||
// if we already have selected model then can safely proceed
|
||||
if (selectedModel) {
|
||||
const assistant = assistants[0]
|
||||
|
||||
console.debug(
|
||||
'Create new thread because user have no thread, with selected model',
|
||||
selectedModel.model
|
||||
)
|
||||
createThreadMutation.mutate({
|
||||
modelId: selectedModel.model,
|
||||
assistant: assistant,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
let modelToBeUsed: Model | undefined = undefined
|
||||
// if we have a model registered already, try to use it and prioritize local model
|
||||
if (models && models.length > 0) {
|
||||
for (const model of models) {
|
||||
if (!model.engine) continue
|
||||
if (LocalEngines.includes(model.engine as LocalEngine)) {
|
||||
modelToBeUsed = model
|
||||
}
|
||||
}
|
||||
|
||||
// if we don't have it, then just take the first one
|
||||
if (!modelToBeUsed) {
|
||||
modelToBeUsed = models[0]
|
||||
}
|
||||
} else {
|
||||
if (!engineData) return
|
||||
// we don't have nay registered model, so will need to check the remote engine
|
||||
const remoteEngineReadyList: Engine[] = []
|
||||
for (const engine of engineData) {
|
||||
if (RemoteEngines.includes(engine.name as RemoteEngine)) {
|
||||
if (engine.status === EngineStatus.Ready) {
|
||||
remoteEngineReadyList.push(engine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remoteEngineReadyList.length === 0) {
|
||||
console.debug("No remote engine ready, can't create thread")
|
||||
return
|
||||
}
|
||||
// find the model from hub that using the engine
|
||||
if (!modelHubData) return
|
||||
const remoteEngineReadyNames = remoteEngineReadyList.map((e) => e.name)
|
||||
|
||||
console.log('remoteEngineReady:', remoteEngineReadyNames)
|
||||
// loop through the modelHubData.modelCategories to find the model that using the engine
|
||||
for (const [key, value] of modelHubData.modelCategories) {
|
||||
if (remoteEngineReadyNames.includes(key) && value.length > 0) {
|
||||
modelToBeUsed = value[0].model
|
||||
if (modelToBeUsed) break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!modelToBeUsed) {
|
||||
console.debug('No model to be used')
|
||||
return
|
||||
}
|
||||
console.log(
|
||||
'Create new thread because user have no thread, model to be used:',
|
||||
modelToBeUsed.model
|
||||
)
|
||||
createModel(modelToBeUsed)
|
||||
setSelectedModel(modelToBeUsed)
|
||||
const assistant = assistants[0]
|
||||
createThreadMutation.mutate({
|
||||
modelId: model.id,
|
||||
modelId: modelToBeUsed.model,
|
||||
assistant: assistant,
|
||||
})
|
||||
}
|
||||
@ -58,11 +161,15 @@ const DataLoader: React.FC = () => {
|
||||
createThreadMutation,
|
||||
allThreads,
|
||||
selectedModel,
|
||||
isAnyModelReady,
|
||||
isAnyRemoteModelConfigured,
|
||||
engineData,
|
||||
modelHubData,
|
||||
setSelectedModel,
|
||||
createModel,
|
||||
])
|
||||
|
||||
useModelHub()
|
||||
useLoadTheme()
|
||||
useEngineQuery()
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@ -2,11 +2,12 @@ import { useCallback, useEffect, useRef } from 'react'
|
||||
|
||||
import { DownloadState2 } from '@janhq/core'
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
import { downloadStateListAtom } from '@/hooks/useDownloadState'
|
||||
|
||||
import useModels from '@/hooks/useModels'
|
||||
import { modelQueryKey } from '@/hooks/useModelQuery'
|
||||
|
||||
import { waitingForCortexAtom } from '@/helpers/atoms/App.atom'
|
||||
import { hostAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||
@ -21,12 +22,12 @@ const DownloadEventListener: React.FC = () => {
|
||||
const abortController = useRef(new AbortController())
|
||||
const setDownloadStateList = useSetAtom(downloadStateListAtom)
|
||||
const setWaitingForCortex = useSetAtom(waitingForCortexAtom)
|
||||
const { getModels } = useModels()
|
||||
|
||||
const updateImportingModelProgress = useSetAtom(
|
||||
updateImportingModelProgressAtom
|
||||
)
|
||||
const setImportingModelSuccess = useSetAtom(setImportingModelSuccessAtom)
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const handleLocalImportModels = useCallback(
|
||||
(events: DownloadState2[]) => {
|
||||
@ -38,9 +39,10 @@ const DownloadEventListener: React.FC = () => {
|
||||
updateImportingModelProgress(event.id, event.progress)
|
||||
}
|
||||
}
|
||||
getModels()
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: modelQueryKey })
|
||||
},
|
||||
[setImportingModelSuccess, updateImportingModelProgress, getModels]
|
||||
[setImportingModelSuccess, updateImportingModelProgress, queryClient]
|
||||
)
|
||||
|
||||
const subscribeDownloadEvent = useCallback(async () => {
|
||||
@ -54,7 +56,6 @@ const DownloadEventListener: React.FC = () => {
|
||||
const localImportEvents: DownloadState2[] = []
|
||||
// filter out the import local events
|
||||
for (const event of downloadEvents) {
|
||||
console.debug('Receiving event', event)
|
||||
if (
|
||||
isAbsolutePath(event.id) &&
|
||||
event.type === 'model' &&
|
||||
|
||||
@ -2,6 +2,7 @@ import React, { Fragment, useCallback, useMemo, useState } from 'react'
|
||||
|
||||
import { Button, Modal, Badge } from '@janhq/joi'
|
||||
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { atom, useAtom, useSetAtom } from 'jotai'
|
||||
import { AlertTriangleIcon } from 'lucide-react'
|
||||
|
||||
@ -11,7 +12,7 @@ import Spinner from '@/containers/Loader/Spinner'
|
||||
|
||||
import useMigratingData from '@/hooks/useMigratingData'
|
||||
|
||||
import useModels from '@/hooks/useModels'
|
||||
import { modelQueryKey } from '@/hooks/useModelQuery'
|
||||
|
||||
import { didShowMigrationWarningAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||
|
||||
@ -31,7 +32,7 @@ const ModalMigrations = () => {
|
||||
useState<MigrationState>('idle')
|
||||
const [modelMigrationState, setModelMigrationState] =
|
||||
useState<MigrationState>('idle')
|
||||
const { getModels } = useModels()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const getStepTitle = () => {
|
||||
switch (step) {
|
||||
@ -74,8 +75,8 @@ const ModalMigrations = () => {
|
||||
setStep(2)
|
||||
await migratingModels()
|
||||
await migrationThreadsAndMessages()
|
||||
getModels()
|
||||
}, [migratingModels, migrationThreadsAndMessages, getModels])
|
||||
queryClient.invalidateQueries({ queryKey: modelQueryKey })
|
||||
}, [migratingModels, migrationThreadsAndMessages, queryClient])
|
||||
|
||||
const onDismiss = useCallback(() => {
|
||||
setStep(1)
|
||||
|
||||
@ -7,10 +7,11 @@ import {
|
||||
StatusAndEvent,
|
||||
} from '@janhq/core'
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
import { removeDownloadSuccessItemAtom } from '@/hooks/useDownloadState'
|
||||
import useModels from '@/hooks/useModels'
|
||||
import { modelQueryKey } from '@/hooks/useModelQuery'
|
||||
|
||||
import { toaster } from '../Toast'
|
||||
|
||||
@ -25,7 +26,7 @@ function ModelEventListener() {
|
||||
const removeDownloadSuccessItem = useSetAtom(removeDownloadSuccessItemAtom)
|
||||
const setIsLoadingModel = useSetAtom(isLoadingModelAtom)
|
||||
|
||||
const { getModels } = useModels()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const handleModelEvent = useCallback(
|
||||
(modelEvent: ModelEvent) => {
|
||||
@ -64,11 +65,11 @@ function ModelEventListener() {
|
||||
|
||||
case 'model-downloaded':
|
||||
removeDownloadSuccessItem(modelEvent.model)
|
||||
getModels()
|
||||
queryClient.invalidateQueries({ queryKey: modelQueryKey })
|
||||
break
|
||||
|
||||
case 'model-deleted':
|
||||
getModels()
|
||||
queryClient.invalidateQueries({ queryKey: modelQueryKey })
|
||||
break
|
||||
|
||||
case 'stopping-failed':
|
||||
@ -84,7 +85,7 @@ function ModelEventListener() {
|
||||
break
|
||||
}
|
||||
},
|
||||
[getModels, removeDownloadSuccessItem, setIsLoadingModel]
|
||||
[removeDownloadSuccessItem, setIsLoadingModel, queryClient]
|
||||
)
|
||||
|
||||
const subscribeModelEvent = useCallback(async () => {
|
||||
|
||||
@ -1,14 +1,8 @@
|
||||
import { RemoteEngine } from '@janhq/core'
|
||||
import { atom } from 'jotai'
|
||||
import { atomWithStorage } from 'jotai/utils'
|
||||
|
||||
export type SetupRemoteModelStage = 'NONE' | 'SETUP_INTRO' | 'SETUP_API_KEY'
|
||||
const IS_ANY_REMOTE_MODEL_CONFIGURED = 'isAnyRemoteModelConfigured'
|
||||
|
||||
export const isAnyRemoteModelConfiguredAtom = atomWithStorage(
|
||||
IS_ANY_REMOTE_MODEL_CONFIGURED,
|
||||
false
|
||||
)
|
||||
const remoteModelSetUpStageAtom = atom<SetupRemoteModelStage>('NONE')
|
||||
const engineBeingSetUpAtom = atom<RemoteEngine | undefined>(undefined)
|
||||
const remoteEngineBeingSetUpMetadataAtom = atom<
|
||||
|
||||
@ -168,11 +168,14 @@ const useModelHub = () => {
|
||||
results[1].data.forEach((modelEntry) => {
|
||||
const engine = modelEntry.engine
|
||||
if (modelEntry.remoteModel === true && engine) {
|
||||
// @ts-expect-error ignore
|
||||
data.modelCategories[engine] = data.modelCategories[engine]
|
||||
? // @ts-expect-error ignore
|
||||
[...data.modelCategories[engine], modelEntry]
|
||||
: [modelEntry]
|
||||
if (data.modelCategories.has(engine)) {
|
||||
data.modelCategories.set(
|
||||
engine,
|
||||
data.modelCategories.get(engine)!.concat(modelEntry)
|
||||
)
|
||||
} else {
|
||||
data.modelCategories.set(engine, [modelEntry])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -6,27 +6,12 @@ import { toaster } from '@/containers/Toast'
|
||||
|
||||
import useCortex from './useCortex'
|
||||
|
||||
import {
|
||||
downloadedModelsAtom,
|
||||
removeDownloadedModelAtom,
|
||||
} from '@/helpers/atoms/Model.atom'
|
||||
import { removeDownloadedModelAtom } from '@/helpers/atoms/Model.atom'
|
||||
|
||||
const useModels = () => {
|
||||
const setDownloadedModels = useSetAtom(downloadedModelsAtom)
|
||||
const removeDownloadedModel = useSetAtom(removeDownloadedModelAtom)
|
||||
const {
|
||||
fetchModels,
|
||||
deleteModel: cortexDeleteModel,
|
||||
updateModel: cortexUpdateModel,
|
||||
} = useCortex()
|
||||
|
||||
const getModels = useCallback(() => {
|
||||
const getDownloadedModels = async () => {
|
||||
const models = await fetchModels()
|
||||
setDownloadedModels(models)
|
||||
}
|
||||
getDownloadedModels()
|
||||
}, [setDownloadedModels, fetchModels])
|
||||
const { deleteModel: cortexDeleteModel, updateModel: cortexUpdateModel } =
|
||||
useCortex()
|
||||
|
||||
const deleteModel = useCallback(
|
||||
async (modelId: string) => {
|
||||
@ -48,7 +33,7 @@ const useModels = () => {
|
||||
[cortexUpdateModel]
|
||||
)
|
||||
|
||||
return { getModels, deleteModel, updateModel }
|
||||
return { deleteModel, updateModel }
|
||||
}
|
||||
|
||||
export default useModels
|
||||
|
||||
@ -3,6 +3,7 @@ import React, { useCallback, useMemo } from 'react'
|
||||
import { EngineStatus, LocalEngines, RemoteEngine } from '@janhq/core'
|
||||
|
||||
import { Button } from '@janhq/joi'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
import { CloudDownload } from 'lucide-react'
|
||||
@ -14,7 +15,7 @@ import useAssistantQuery from '@/hooks/useAssistantQuery'
|
||||
import useCortex from '@/hooks/useCortex'
|
||||
|
||||
import useEngineQuery from '@/hooks/useEngineQuery'
|
||||
import useModels from '@/hooks/useModels'
|
||||
import { modelQueryKey } from '@/hooks/useModelQuery'
|
||||
import useThreads from '@/hooks/useThreads'
|
||||
|
||||
import { HfModelEntry } from '@/utils/huggingface'
|
||||
@ -32,6 +33,7 @@ const HubModelCard: React.FC<HfModelEntry> = ({ name, downloads, model }) => {
|
||||
const downloadedModels = useAtomValue(downloadedModelsAtom)
|
||||
const { data: assistants } = useAssistantQuery()
|
||||
const { data: engineData } = useEngineQuery()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const setUpRemoteModelStage = useSetAtom(setUpRemoteModelStageAtom)
|
||||
const setLocalModelModalStage = useSetAtom(localModelModalStageAtom)
|
||||
@ -39,7 +41,6 @@ const HubModelCard: React.FC<HfModelEntry> = ({ name, downloads, model }) => {
|
||||
const { createThread } = useThreads()
|
||||
const setMainViewState = useSetAtom(mainViewStateAtom)
|
||||
const { createModel } = useCortex()
|
||||
const { getModels } = useModels()
|
||||
|
||||
const isLocalModel = useMemo(
|
||||
() =>
|
||||
@ -131,13 +132,12 @@ const HubModelCard: React.FC<HfModelEntry> = ({ name, downloads, model }) => {
|
||||
|
||||
if (isApiKeyAdded) {
|
||||
createModel(model).then(() => {
|
||||
getModels()
|
||||
queryClient.invalidateQueries({ queryKey: modelQueryKey })
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}, [
|
||||
getModels,
|
||||
createModel,
|
||||
createThread,
|
||||
setMainViewState,
|
||||
@ -149,6 +149,7 @@ const HubModelCard: React.FC<HfModelEntry> = ({ name, downloads, model }) => {
|
||||
isLocalModel,
|
||||
downloadedModels,
|
||||
assistants,
|
||||
queryClient,
|
||||
])
|
||||
|
||||
const owner = model?.metadata?.owned_by ?? ''
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
import { EngineStatus, RemoteEngine } from '@janhq/core'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
import { toaster } from '@/containers/Toast'
|
||||
@ -10,7 +12,7 @@ import useAssistantQuery from '@/hooks/useAssistantQuery'
|
||||
import useCortex from '@/hooks/useCortex'
|
||||
|
||||
import useEngineQuery from '@/hooks/useEngineQuery'
|
||||
import useModels from '@/hooks/useModels'
|
||||
import { modelQueryKey } from '@/hooks/useModelQuery'
|
||||
import useThreads from '@/hooks/useThreads'
|
||||
|
||||
import { HfModelEntry } from '@/utils/huggingface'
|
||||
@ -23,12 +25,11 @@ const RemoteModelCard: React.FC<HfModelEntry> = ({ name, model }) => {
|
||||
const { createThread } = useThreads()
|
||||
const setMainViewState = useSetAtom(mainViewStateAtom)
|
||||
const setUpRemoteModelStage = useSetAtom(setUpRemoteModelStageAtom)
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { createModel } = useCortex()
|
||||
const { getModels } = useModels()
|
||||
const downloadedModels = useAtomValue(downloadedModelsAtom)
|
||||
const { data: assistants } = useAssistantQuery()
|
||||
|
||||
const { data: engineData } = useEngineQuery()
|
||||
|
||||
const modelDisplayName = model?.name ?? name
|
||||
@ -85,7 +86,7 @@ const RemoteModelCard: React.FC<HfModelEntry> = ({ name, model }) => {
|
||||
if (isApiKeyAdded) {
|
||||
// TODO: useMutation reactQuery?
|
||||
await createModel(model)
|
||||
getModels()
|
||||
queryClient.invalidateQueries({ queryKey: modelQueryKey })
|
||||
if (!assistants || assistants.length === 0) {
|
||||
toaster({
|
||||
title: 'No assistant available.',
|
||||
@ -109,11 +110,11 @@ const RemoteModelCard: React.FC<HfModelEntry> = ({ name, model }) => {
|
||||
createModel,
|
||||
createThread,
|
||||
downloadedModels,
|
||||
getModels,
|
||||
model,
|
||||
setMainViewState,
|
||||
setUpRemoteModelStage,
|
||||
modelDisplayName,
|
||||
queryClient,
|
||||
])
|
||||
|
||||
return (
|
||||
|
||||
@ -4,7 +4,7 @@ import Image from 'next/image'
|
||||
|
||||
import { EngineStatus } from '@janhq/core'
|
||||
import { Button, Input, Modal } from '@janhq/joi'
|
||||
import { useAtom, useSetAtom } from 'jotai'
|
||||
import { useAtom } from 'jotai'
|
||||
import { ArrowUpRight } from 'lucide-react'
|
||||
|
||||
import useEngineMutation from '@/hooks/useEngineMutation'
|
||||
@ -12,13 +12,10 @@ import useEngineQuery from '@/hooks/useEngineQuery'
|
||||
|
||||
import { getTitleByCategory } from '@/utils/model-engine'
|
||||
|
||||
import { isAnyRemoteModelConfiguredAtom } from '@/helpers/atoms/SetupRemoteModel.atom'
|
||||
|
||||
import { setUpRemoteModelStageAtom } from '@/helpers/atoms/SetupRemoteModel.atom'
|
||||
|
||||
const SetUpApiKeyModal: React.FC = () => {
|
||||
const updateEngineConfig = useEngineMutation()
|
||||
const isAnyRemoteModelConfigured = useSetAtom(isAnyRemoteModelConfiguredAtom)
|
||||
const { data: engineData } = useEngineQuery()
|
||||
|
||||
const [{ stage, remoteEngine, metadata }, setUpRemoteModelStage] = useAtom(
|
||||
@ -50,8 +47,7 @@ const SetUpApiKeyModal: React.FC = () => {
|
||||
value: apiKey,
|
||||
},
|
||||
})
|
||||
isAnyRemoteModelConfigured(true)
|
||||
}, [remoteEngine, updateEngineConfig, apiKey, isAnyRemoteModelConfigured])
|
||||
}, [remoteEngine, updateEngineConfig, apiKey])
|
||||
|
||||
const onDismiss = useCallback(() => {
|
||||
setUpRemoteModelStage('NONE', undefined)
|
||||
|
||||
@ -44,11 +44,11 @@ const HubScreen2: React.FC = () => {
|
||||
if (!data) return <div>Failed to fetch models</div>
|
||||
|
||||
const engineModelMap = new Map<typeof RemoteEngines, HfModelEntry[]>()
|
||||
Object.entries(data.modelCategories).forEach(([key, value]) => {
|
||||
for (const [key, value] of data.modelCategories) {
|
||||
if (key !== 'HuggingFace' && key !== 'BuiltInModels') {
|
||||
engineModelMap.set(key as unknown as typeof RemoteEngines, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (detailCategory) {
|
||||
return (
|
||||
|
||||
@ -21,6 +21,7 @@ import { twMerge } from 'tailwind-merge'
|
||||
import { toaster } from '@/containers/Toast'
|
||||
|
||||
import useAssistantQuery from '@/hooks/useAssistantQuery'
|
||||
import useEngineInit from '@/hooks/useEngineInit'
|
||||
import useEngineQuery from '@/hooks/useEngineQuery'
|
||||
import useModelStart from '@/hooks/useModelStart'
|
||||
import useModelStop from '@/hooks/useModelStop'
|
||||
@ -54,6 +55,7 @@ const ModelItem: React.FC<Props> = ({ model }) => {
|
||||
const isEngineReady =
|
||||
engineData?.find((e) => e.name === model.engine)?.status ===
|
||||
EngineStatus.Ready
|
||||
const initializeEngine = useEngineInit()
|
||||
|
||||
const [menu, setMenu] = useState<HTMLDivElement | null>(null)
|
||||
const [toggle, setToggle] = useState<HTMLDivElement | null>(null)
|
||||
@ -74,6 +76,46 @@ const ModelItem: React.FC<Props> = ({ model }) => {
|
||||
stopModel.mutate(modelId)
|
||||
return
|
||||
}
|
||||
const modelEngine = model.engine
|
||||
if (!modelEngine) {
|
||||
toaster({
|
||||
title: 'Failed to start model',
|
||||
description: `Engine for model ${model.model} is undefined`,
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!engineData) {
|
||||
toaster({
|
||||
title: 'Failed to start model',
|
||||
description: `Engine data is not available. Please try again!`,
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
const engineStatus = engineData.find((e) => e.name === modelEngine)
|
||||
if (!engineStatus) {
|
||||
toaster({
|
||||
title: 'Failed to start model',
|
||||
description: `Engine ${modelEngine} is not available`,
|
||||
type: 'error',
|
||||
})
|
||||
console.error(`Engine ${modelEngine} is not available`)
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
LocalEngines.find((e) => e === modelEngine) != null &&
|
||||
engineStatus.status === 'not_initialized'
|
||||
) {
|
||||
toaster({
|
||||
title: 'Please wait for engine to initialize',
|
||||
description: `Please retry after engine ${engineStatus.name} is installed.`,
|
||||
type: 'default',
|
||||
})
|
||||
initializeEngine.mutate(modelEngine)
|
||||
return
|
||||
}
|
||||
|
||||
if (activeModels.length >= concurrentModelWarningThreshold) {
|
||||
// if max concurrent models reached, stop the first model
|
||||
@ -88,6 +130,9 @@ const ModelItem: React.FC<Props> = ({ model }) => {
|
||||
stopModel,
|
||||
activeModels.length,
|
||||
setShowWarningMultipleModelModal,
|
||||
engineData,
|
||||
initializeEngine,
|
||||
model,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@ -53,11 +53,11 @@ const OnDeviceStarterScreen = () => {
|
||||
data.modelCategories.get('HuggingFace') || []
|
||||
|
||||
const engineModelMap = new Map<typeof RemoteEngines, HfModelEntry[]>()
|
||||
Object.entries(data.modelCategories).forEach(([key, value]) => {
|
||||
for (const [key, value] of data.modelCategories) {
|
||||
if (key !== 'HuggingFace' && key !== 'BuiltInModels') {
|
||||
engineModelMap.set(key as unknown as typeof RemoteEngines, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const models: HfModelEntry[] = builtInModels.concat(huggingFaceModels)
|
||||
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { Fragment, useEffect } from 'react'
|
||||
import { Fragment, useMemo } from 'react'
|
||||
|
||||
import { Model } from '@janhq/core'
|
||||
import { useAtom, useAtomValue } from 'jotai'
|
||||
import {
|
||||
EngineStatus,
|
||||
LlmEngine,
|
||||
RemoteEngine,
|
||||
RemoteEngines,
|
||||
} from '@janhq/core'
|
||||
import { useAtomValue } from 'jotai'
|
||||
|
||||
import useCortex from '@/hooks/useCortex'
|
||||
import useModels from '@/hooks/useModels'
|
||||
import useEngineQuery from '@/hooks/useEngineQuery'
|
||||
|
||||
import ThreadLeftPanel from '@/screens/Thread/ThreadLeftPanel'
|
||||
|
||||
@ -14,43 +18,44 @@ import ThreadRightPanel from './ThreadRightPanel'
|
||||
|
||||
import { waitingForCortexAtom } from '@/helpers/atoms/App.atom'
|
||||
import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom'
|
||||
import {
|
||||
isAnyRemoteModelConfiguredAtom,
|
||||
setUpRemoteModelStageAtom,
|
||||
} from '@/helpers/atoms/SetupRemoteModel.atom'
|
||||
|
||||
const ThreadScreen = () => {
|
||||
const downloadedModels = useAtomValue(downloadedModelsAtom)
|
||||
const waitingForCortex = useAtomValue(waitingForCortexAtom)
|
||||
const isAnyRemoteModelConfigured = useAtomValue(
|
||||
isAnyRemoteModelConfiguredAtom
|
||||
)
|
||||
const { createModel } = useCortex()
|
||||
const { getModels } = useModels()
|
||||
const { data: engineData } = useEngineQuery()
|
||||
|
||||
const [{ metadata }] = useAtom(setUpRemoteModelStageAtom)
|
||||
const isAnyRemoteModelConfigured = useMemo(() => {
|
||||
if (!engineData) return false
|
||||
|
||||
useEffect(() => {
|
||||
if (isAnyRemoteModelConfigured) {
|
||||
createModel(metadata?.model as Model)
|
||||
getModels()
|
||||
let result = false
|
||||
for (const engine of engineData) {
|
||||
if (RemoteEngines.includes(engine.name as RemoteEngine)) {
|
||||
if (engine.status === EngineStatus.Ready) {
|
||||
result = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isAnyRemoteModelConfigured])
|
||||
return result
|
||||
}, [engineData])
|
||||
|
||||
const shouldShowEmptyModel = useMemo(
|
||||
() => !downloadedModels.length && !isAnyRemoteModelConfigured,
|
||||
[downloadedModels, isAnyRemoteModelConfigured]
|
||||
)
|
||||
|
||||
if (waitingForCortex) return null
|
||||
|
||||
return (
|
||||
<div className="relative flex h-full w-full flex-1 overflow-x-hidden">
|
||||
{!downloadedModels.length && !isAnyRemoteModelConfigured ? (
|
||||
{shouldShowEmptyModel ? (
|
||||
<EmptyModel />
|
||||
) : (
|
||||
<Fragment>
|
||||
<ThreadLeftPanel />
|
||||
<ThreadCenterPanel />
|
||||
<ThreadRightPanel />
|
||||
</Fragment>
|
||||
)}
|
||||
<ThreadRightPanel />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -106,7 +106,6 @@ export const fetchCortexHubModels = async (): Promise<HfModelEntry[]> => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modelEntries
|
||||
}
|
||||
|
||||
@ -333,9 +332,8 @@ export const fetchHuggingFaceRepoData = async (
|
||||
return data
|
||||
}
|
||||
|
||||
// TODO: move this to somewhere else
|
||||
export interface HfModelEntry extends ModelEntry {
|
||||
model?: Model // TODO: deprecated this
|
||||
model?: Model
|
||||
remoteModel?: boolean
|
||||
engine?: LlmEngine
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user