Skip to content

Commit

Permalink
fix: empty model page not shown when delete all threads and models (#…
Browse files Browse the repository at this point in the history
…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>
  • Loading branch information
namchuai and Van-QA authored Aug 12, 2024
1 parent fdab8af commit 9e29fcd
Show file tree
Hide file tree
Showing 16 changed files with 241 additions and 103 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
129 changes: 118 additions & 11 deletions web/containers/Providers/DataLoader.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
'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'
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
Expand All @@ -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

if (shouldCreateNewThread && !createThreadMutation.isPending) {
// if we already have selected model then can safely proceed
if (selectedModel) {
const assistant = assistants[0]

console.log('Create new thread because user have no thread')
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,
})
}
Expand All @@ -58,11 +161,15 @@ const DataLoader: React.FC = () => {
createThreadMutation,
allThreads,
selectedModel,
isAnyModelReady,
isAnyRemoteModelConfigured,
engineData,
modelHubData,
setSelectedModel,
createModel,
])

useModelHub()
useLoadTheme()
useEngineQuery()

return null
}
Expand Down
11 changes: 6 additions & 5 deletions web/containers/Providers/DownloadEventListener.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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[]) => {
Expand All @@ -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 () => {
Expand All @@ -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' &&
Expand Down
9 changes: 5 additions & 4 deletions web/containers/Providers/ModalMigrations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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'

Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 6 additions & 5 deletions web/containers/Providers/ModelEventListener.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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) => {
Expand Down Expand Up @@ -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':
Expand All @@ -84,7 +85,7 @@ function ModelEventListener() {
break
}
},
[getModels, removeDownloadSuccessItem, setIsLoadingModel]
[removeDownloadSuccessItem, setIsLoadingModel, queryClient]
)

const subscribeModelEvent = useCallback(async () => {
Expand Down
6 changes: 0 additions & 6 deletions web/helpers/atoms/SetupRemoteModel.atom.ts
Original file line number Diff line number Diff line change
@@ -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<
Expand Down
13 changes: 8 additions & 5 deletions web/hooks/useModelHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
}
})
}
Expand Down
23 changes: 4 additions & 19 deletions web/hooks/useModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -48,7 +33,7 @@ const useModels = () => {
[cortexUpdateModel]
)

return { getModels, deleteModel, updateModel }
return { deleteModel, updateModel }
}

export default useModels
Loading

0 comments on commit 9e29fcd

Please sign in to comment.