fix: set cortex data folder path when starting jan (#3252)
* fix: set cortex data folder path when starting jan * fix: change port to 1338 * fix: add migration in advanced setting * update * update new cortex * feat: add import model error handler Signed-off-by: James <namnh0122@gmail.com> --------- Signed-off-by: James <namnh0122@gmail.com>
This commit is contained in:
parent
224ca3f7cc
commit
91c77eda78
@ -129,4 +129,5 @@ export interface DownloadStateEvent {
|
|||||||
export enum DownloadType2 {
|
export enum DownloadType2 {
|
||||||
Model = 'model',
|
Model = 'model',
|
||||||
Miscelanous = 'miscelanous',
|
Miscelanous = 'miscelanous',
|
||||||
|
Engine = 'engine',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,16 +8,15 @@ import {
|
|||||||
} from '@janhq/core/node'
|
} from '@janhq/core/node'
|
||||||
import { menu } from '../utils/menu'
|
import { menu } from '../utils/menu'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { getJanDataFolderPath } from './../utils/path'
|
import { getAppConfigurations, getJanDataFolderPath } from './../utils/path'
|
||||||
import {
|
import {
|
||||||
readdirSync,
|
readdirSync,
|
||||||
writeFileSync,
|
writeFileSync,
|
||||||
readFileSync,
|
readFileSync,
|
||||||
existsSync,
|
existsSync,
|
||||||
mkdirSync
|
mkdirSync,
|
||||||
} from 'fs'
|
} from 'fs'
|
||||||
import { dump } from 'js-yaml'
|
import { dump } from 'js-yaml'
|
||||||
import os from 'os'
|
|
||||||
|
|
||||||
const isMac = process.platform === 'darwin'
|
const isMac = process.platform === 'darwin'
|
||||||
|
|
||||||
@ -178,7 +177,7 @@ export function handleAppIPCs() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
ipcMain.handle(NativeRoute.showOpenMenu, function (e, args) {
|
ipcMain.handle(NativeRoute.showOpenMenu, function (_e, args) {
|
||||||
if (!isMac && windowManager.mainWindow) {
|
if (!isMac && windowManager.mainWindow) {
|
||||||
menu.popup({
|
menu.popup({
|
||||||
window: windowManager.mainWindow,
|
window: windowManager.mainWindow,
|
||||||
@ -209,10 +208,11 @@ export function handleAppIPCs() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle(NativeRoute.openAppLog, async (_event): Promise<void> => {
|
ipcMain.handle(NativeRoute.openAppLog, async (_event): Promise<void> => {
|
||||||
const cortexHomeDir = join(os.homedir(), 'cortex')
|
const configuration = getAppConfigurations()
|
||||||
|
const dataFolder = configuration.data_folder
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const errorMessage = await shell.openPath(join(cortexHomeDir))
|
const errorMessage = await shell.openPath(join(dataFolder))
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
console.error(`An error occurred: ${errorMessage}`)
|
console.error(`An error occurred: ${errorMessage}`)
|
||||||
} else {
|
} else {
|
||||||
@ -227,21 +227,19 @@ export function handleAppIPCs() {
|
|||||||
const janModelFolderPath = join(getJanDataFolderPath(), 'models')
|
const janModelFolderPath = join(getJanDataFolderPath(), 'models')
|
||||||
const allModelFolders = readdirSync(janModelFolderPath)
|
const allModelFolders = readdirSync(janModelFolderPath)
|
||||||
|
|
||||||
const cortexHomeDir = join(os.homedir(), 'cortex')
|
const configration = getAppConfigurations()
|
||||||
const cortexModelFolderPath = join(cortexHomeDir, 'models')
|
const destinationFolderPath = join(configration.data_folder, 'models')
|
||||||
|
|
||||||
if(!existsSync(cortexModelFolderPath))
|
if (!existsSync(destinationFolderPath)) mkdirSync(destinationFolderPath)
|
||||||
mkdirSync(cortexModelFolderPath)
|
console.log('destinationFolderPath', destinationFolderPath)
|
||||||
console.log('cortexModelFolderPath', cortexModelFolderPath)
|
|
||||||
const reflect = require('@alumna/reflect')
|
const reflect = require('@alumna/reflect')
|
||||||
|
|
||||||
for (const modelName of allModelFolders) {
|
for (const modelName of allModelFolders) {
|
||||||
const modelFolderPath = join(janModelFolderPath, modelName)
|
const modelFolderPath = join(janModelFolderPath, modelName)
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const filesInModelFolder = readdirSync(modelFolderPath)
|
const filesInModelFolder = readdirSync(modelFolderPath)
|
||||||
|
|
||||||
const destinationPath = join(cortexModelFolderPath, modelName)
|
const destinationPath = join(destinationFolderPath, modelName)
|
||||||
|
|
||||||
const modelJsonFullPath = join(
|
const modelJsonFullPath = join(
|
||||||
janModelFolderPath,
|
janModelFolderPath,
|
||||||
@ -253,15 +251,18 @@ export function handleAppIPCs() {
|
|||||||
const fileNames: string[] = model.sources.map((x: any) => x.filename)
|
const fileNames: string[] = model.sources.map((x: any) => x.filename)
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
|
|
||||||
if(filesInModelFolder.length > 1) {
|
if (filesInModelFolder.length > 1) {
|
||||||
// prepend fileNames with cortexModelFolderPath
|
// prepend fileNames with model folder path
|
||||||
files = fileNames.map((x: string) =>
|
files = fileNames.map((x: string) =>
|
||||||
join(cortexModelFolderPath, model.id, x)
|
join(destinationFolderPath, model.id, x)
|
||||||
)
|
)
|
||||||
} else if(model.sources.length && !/^(http|https):\/\/[^/]+\/.*/.test(model.sources[0].url)) {
|
} else if (
|
||||||
|
model.sources.length &&
|
||||||
|
!/^(http|https):\/\/[^/]+\/.*/.test(model.sources[0].url)
|
||||||
|
) {
|
||||||
// Symlink case
|
// Symlink case
|
||||||
files = [ model.sources[0].url ]
|
files = [model.sources[0].url]
|
||||||
} else continue;
|
} else continue
|
||||||
|
|
||||||
// create folder if not exist
|
// create folder if not exist
|
||||||
// only for local model files
|
// only for local model files
|
||||||
@ -269,7 +270,10 @@ export function handleAppIPCs() {
|
|||||||
mkdirSync(destinationPath, { recursive: true })
|
mkdirSync(destinationPath, { recursive: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
const engine = (model.engine === 'nitro' || model.engine === 'cortex') ? 'cortex.llamacpp' : (model.engine ?? 'cortex.llamacpp')
|
const engine =
|
||||||
|
model.engine === 'nitro' || model.engine === 'cortex'
|
||||||
|
? 'cortex.llamacpp'
|
||||||
|
: (model.engine ?? 'cortex.llamacpp')
|
||||||
|
|
||||||
const updatedModelFormat = {
|
const updatedModelFormat = {
|
||||||
id: model.id,
|
id: model.id,
|
||||||
@ -296,7 +300,7 @@ export function handleAppIPCs() {
|
|||||||
max_tokens: model.parameters?.max_tokens ?? 2048,
|
max_tokens: model.parameters?.max_tokens ?? 2048,
|
||||||
stream: model.parameters?.stream ?? true,
|
stream: model.parameters?.stream ?? true,
|
||||||
}
|
}
|
||||||
if(filesInModelFolder.length > 1 ) {
|
if (filesInModelFolder.length > 1) {
|
||||||
const { err } = await reflect({
|
const { err } = await reflect({
|
||||||
src: modelFolderPath,
|
src: modelFolderPath,
|
||||||
dest: destinationPath,
|
dest: destinationPath,
|
||||||
@ -308,13 +312,13 @@ export function handleAppIPCs() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// create the model.yml file
|
// create the model.yml file
|
||||||
const modelYamlData = dump(updatedModelFormat)
|
const modelYamlData = dump(updatedModelFormat)
|
||||||
const modelYamlPath = join(cortexModelFolderPath, `${modelName}.yaml`)
|
const modelYamlPath = join(destinationFolderPath, `${modelName}.yaml`)
|
||||||
|
|
||||||
writeFileSync(modelYamlPath, modelYamlData)
|
writeFileSync(modelYamlPath, modelYamlData)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -354,7 +358,7 @@ export function handleAppIPCs() {
|
|||||||
'messages.jsonl'
|
'messages.jsonl'
|
||||||
)
|
)
|
||||||
|
|
||||||
if(!existsSync(messageFullPath)) continue;
|
if (!existsSync(messageFullPath)) continue
|
||||||
const lines = readFileSync(messageFullPath, 'utf-8')
|
const lines = readFileSync(messageFullPath, 'utf-8')
|
||||||
.toString()
|
.toString()
|
||||||
.split('\n')
|
.split('\n')
|
||||||
@ -379,8 +383,10 @@ export function handleAppIPCs() {
|
|||||||
const janModelsFolderPath = join(getJanDataFolderPath(), 'models')
|
const janModelsFolderPath = join(getJanDataFolderPath(), 'models')
|
||||||
|
|
||||||
if (!existsSync(janModelsFolderPath)) {
|
if (!existsSync(janModelsFolderPath)) {
|
||||||
|
console.debug('No local models found')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// get children of thread folder
|
// get children of thread folder
|
||||||
const allModelsFolders = readdirSync(janModelsFolderPath)
|
const allModelsFolders = readdirSync(janModelsFolderPath)
|
||||||
let hasLocalModels = false
|
let hasLocalModels = false
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { handleAppIPCs } from './handlers/native'
|
|||||||
* Utils
|
* Utils
|
||||||
**/
|
**/
|
||||||
import { setupMenu } from './utils/menu'
|
import { setupMenu } from './utils/menu'
|
||||||
import { createUserSpace } from './utils/path'
|
import { createUserSpace, getAppConfigurations } from './utils/path'
|
||||||
import { migrate } from './utils/migration'
|
import { migrate } from './utils/migration'
|
||||||
import { cleanUpAndQuit } from './utils/clean'
|
import { cleanUpAndQuit } from './utils/clean'
|
||||||
import { setupCore } from './utils/setup'
|
import { setupCore } from './utils/setup'
|
||||||
@ -58,12 +58,31 @@ Object.assign(console, log.functions)
|
|||||||
|
|
||||||
let cortexService: ChildProcess | undefined = undefined
|
let cortexService: ChildProcess | undefined = undefined
|
||||||
|
|
||||||
|
const cortexJsPort = 1338
|
||||||
|
const cortexCppPort = 3940
|
||||||
|
const host = '127.0.0.1'
|
||||||
|
|
||||||
app
|
app
|
||||||
.whenReady()
|
.whenReady()
|
||||||
.then(() => killProcessesOnPort(3929))
|
.then(() => killProcessesOnPort(cortexCppPort))
|
||||||
.then(() => killProcessesOnPort(1337))
|
.then(() => killProcessesOnPort(cortexJsPort))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const command = `${cortexPath} -a 127.0.0.1 -p 1337`
|
const appConfiguration = getAppConfigurations()
|
||||||
|
const janDataFolder = appConfiguration.data_folder
|
||||||
|
|
||||||
|
const cortexParams: Record<string, string> = {
|
||||||
|
'-n': 'jan',
|
||||||
|
'-a': host,
|
||||||
|
'-p': cortexJsPort.toString(),
|
||||||
|
'-ep': cortexCppPort.toString(),
|
||||||
|
'--dataFolder': janDataFolder,
|
||||||
|
}
|
||||||
|
|
||||||
|
// add cortex parameters to the command
|
||||||
|
const command = Object.entries(cortexParams).reduce(
|
||||||
|
(acc, [key, value]) => `${acc} ${key} ${value}`,
|
||||||
|
`${cortexPath}`
|
||||||
|
)
|
||||||
|
|
||||||
log.info('Starting cortex with command:', command)
|
log.info('Starting cortex with command:', command)
|
||||||
// init cortex
|
// init cortex
|
||||||
@ -154,7 +173,7 @@ async function stopCortexService() {
|
|||||||
async function stopApiServer() {
|
async function stopApiServer() {
|
||||||
// this function is not meant to be success. It will throw an error.
|
// this function is not meant to be success. It will throw an error.
|
||||||
try {
|
try {
|
||||||
await fetch('http://localhost:1337/v1/system', {
|
await fetch(`http://${host}:${cortexJsPort}/v1/system`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
0.5.0-27
|
0.5.0-29
|
||||||
|
|||||||
@ -40,16 +40,21 @@ const DownloadStatus: React.FC = () => {
|
|||||||
? ((totalTransfferedSize / totalDownloadSize) * 100).toFixed(2)
|
? ((totalTransfferedSize / totalDownloadSize) * 100).toFixed(2)
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
|
const downloadTitle = `Downloading ${downloadStates
|
||||||
|
.map((state) => state.type)
|
||||||
|
.join(', ')
|
||||||
|
.trim()}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{Object.values(downloadStates)?.length > 0 && (
|
{Object.values(downloadStates)?.length > 0 && (
|
||||||
<Modal
|
<Modal
|
||||||
title="Downloading model"
|
title={downloadTitle}
|
||||||
trigger={
|
trigger={
|
||||||
<div className="flex cursor-pointer items-center gap-2">
|
<div className="flex cursor-pointer items-center gap-2">
|
||||||
<Button size="small" theme="ghost">
|
<Button size="small" theme="ghost">
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
Downloading model{' '}
|
{downloadTitle}{' '}
|
||||||
{Object.values(downloadStates).length > 1 &&
|
{Object.values(downloadStates).length > 1 &&
|
||||||
`1/${Object.values(downloadStates).length}`}
|
`1/${Object.values(downloadStates).length}`}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import TopPanel from '@/containers/Layout/TopPanel'
|
|||||||
|
|
||||||
import { getImportModelStageAtom } from '@/hooks/useImportModel'
|
import { getImportModelStageAtom } from '@/hooks/useImportModel'
|
||||||
|
|
||||||
|
import useMigratingData from '@/hooks/useMigratingData'
|
||||||
|
|
||||||
import DownloadLocalModelModal from '@/screens/HubScreen2/components/DownloadLocalModelModal'
|
import DownloadLocalModelModal from '@/screens/HubScreen2/components/DownloadLocalModelModal'
|
||||||
import InferenceErrorModal from '@/screens/HubScreen2/components/InferenceErrorModal'
|
import InferenceErrorModal from '@/screens/HubScreen2/components/InferenceErrorModal'
|
||||||
import SetUpApiKeyModal from '@/screens/HubScreen2/components/SetUpApiKeyModal'
|
import SetUpApiKeyModal from '@/screens/HubScreen2/components/SetUpApiKeyModal'
|
||||||
@ -33,17 +35,24 @@ import LoadingModal from '../LoadingModal'
|
|||||||
|
|
||||||
import MainViewContainer from '../MainViewContainer'
|
import MainViewContainer from '../MainViewContainer'
|
||||||
|
|
||||||
|
import ModalMigrations, {
|
||||||
|
showMigrationModalAtom,
|
||||||
|
} from '../Providers/ModalMigrations'
|
||||||
import WaitingForCortexModal from '../WaitingCortexModal'
|
import WaitingForCortexModal from '../WaitingCortexModal'
|
||||||
|
|
||||||
import InstallingExtensionModal from './BottomPanel/InstallingExtension/InstallingExtensionModal'
|
import InstallingExtensionModal from './BottomPanel/InstallingExtension/InstallingExtensionModal'
|
||||||
|
|
||||||
import { MainViewState, mainViewStateAtom } from '@/helpers/atoms/App.atom'
|
import { MainViewState, mainViewStateAtom } from '@/helpers/atoms/App.atom'
|
||||||
|
import { didShowMigrationWarningAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||||
import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
||||||
|
|
||||||
const BaseLayout = () => {
|
const BaseLayout = () => {
|
||||||
|
const didShowMigrationWarning = useAtomValue(didShowMigrationWarningAtom)
|
||||||
|
const setShowMigrationModal = useSetAtom(showMigrationModalAtom)
|
||||||
const setMainViewState = useSetAtom(mainViewStateAtom)
|
const setMainViewState = useSetAtom(mainViewStateAtom)
|
||||||
const importModelStage = useAtomValue(getImportModelStageAtom)
|
const importModelStage = useAtomValue(getImportModelStageAtom)
|
||||||
const reduceTransparent = useAtomValue(reduceTransparentAtom)
|
const reduceTransparent = useAtomValue(reduceTransparentAtom)
|
||||||
|
const { getJanThreadsAndMessages, getJanLocalModels } = useMigratingData()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localStorage.getItem(SUCCESS_SET_NEW_DESTINATION) === 'true') {
|
if (localStorage.getItem(SUCCESS_SET_NEW_DESTINATION) === 'true') {
|
||||||
@ -51,6 +60,29 @@ const BaseLayout = () => {
|
|||||||
}
|
}
|
||||||
}, [setMainViewState])
|
}, [setMainViewState])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (didShowMigrationWarning) return
|
||||||
|
|
||||||
|
const isUserHaveData = async (): Promise<boolean> => {
|
||||||
|
const threadAndMessageData = await getJanThreadsAndMessages()
|
||||||
|
const isUserHaveAnyModel = await getJanLocalModels()
|
||||||
|
return threadAndMessageData.threads.length > 0 || isUserHaveAnyModel
|
||||||
|
}
|
||||||
|
|
||||||
|
isUserHaveData()
|
||||||
|
.then((isUserHaveData) => {
|
||||||
|
if (isUserHaveData === true) {
|
||||||
|
setShowMigrationModal(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => console.error('Error checking user data', e))
|
||||||
|
}, [
|
||||||
|
didShowMigrationWarning,
|
||||||
|
getJanThreadsAndMessages,
|
||||||
|
getJanLocalModels,
|
||||||
|
setShowMigrationModal,
|
||||||
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
@ -92,6 +124,7 @@ const BaseLayout = () => {
|
|||||||
<ChooseWhatToImportModal />
|
<ChooseWhatToImportModal />
|
||||||
<InstallingExtensionModal />
|
<InstallingExtensionModal />
|
||||||
<HuggingFaceRepoDetailModal />
|
<HuggingFaceRepoDetailModal />
|
||||||
|
<ModalMigrations />
|
||||||
</div>
|
</div>
|
||||||
<BottomPanel />
|
<BottomPanel />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import React, { Fragment, useCallback, useEffect } from 'react'
|
import React, { Fragment, useCallback, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { Button, Modal, Badge } from '@janhq/joi'
|
import { Button, Modal, Badge } from '@janhq/joi'
|
||||||
|
|
||||||
import { useAtom, useAtomValue } from 'jotai'
|
import { atom, useAtom, useSetAtom } from 'jotai'
|
||||||
import { AlertTriangleIcon } from 'lucide-react'
|
import { AlertTriangleIcon } from 'lucide-react'
|
||||||
|
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
@ -11,25 +11,24 @@ import Spinner from '@/containers/Loader/Spinner'
|
|||||||
|
|
||||||
import useMigratingData from '@/hooks/useMigratingData'
|
import useMigratingData from '@/hooks/useMigratingData'
|
||||||
|
|
||||||
import {
|
import { didShowMigrationWarningAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||||
didShowMigrationWarningAtom,
|
|
||||||
modelsMigrationSuccessAtom,
|
export const showMigrationModalAtom = atom<boolean>(false)
|
||||||
threadsMessagesMigrationSuccessAtom,
|
|
||||||
skipMigrationAtom,
|
const MigrationStates = ['idle', 'in_progress', 'failed', 'success'] as const
|
||||||
} from '@/helpers/atoms/AppConfig.atom'
|
type MigrationState = (typeof MigrationStates)[number]
|
||||||
|
|
||||||
const ModalMigrations = () => {
|
const ModalMigrations = () => {
|
||||||
const [didShowMigrationWarning, setDidShowMigrationWarning] = useAtom(
|
const setDidShowMigrationModal = useSetAtom(didShowMigrationWarningAtom)
|
||||||
didShowMigrationWarningAtom
|
const [showMigrationModal, setShowMigrationModal] = useAtom(
|
||||||
)
|
showMigrationModalAtom
|
||||||
const [skipMigration, setSkipMigration] = useAtom(skipMigrationAtom)
|
|
||||||
const modelsMigrationSuccess = useAtomValue(modelsMigrationSuccessAtom)
|
|
||||||
const threadsMessagesMigrationSuccess = useAtomValue(
|
|
||||||
threadsMessagesMigrationSuccessAtom
|
|
||||||
)
|
)
|
||||||
const [step, setStep] = React.useState(1)
|
const [step, setStep] = React.useState(1)
|
||||||
const [loaderThreads, setLoaderThreads] = React.useState(false)
|
const { migrateModels, migrateThreadsAndMessages } = useMigratingData()
|
||||||
const [loaderModels, setLoaderModels] = React.useState(false)
|
const [threadAndMessageMigrationState, setThreadAndMessageMigrationState] =
|
||||||
|
useState<MigrationState>('idle')
|
||||||
|
const [modelMigrationState, setModelMigrationState] =
|
||||||
|
useState<MigrationState>('idle')
|
||||||
|
|
||||||
const getStepTitle = () => {
|
const getStepTitle = () => {
|
||||||
switch (step) {
|
switch (step) {
|
||||||
@ -37,84 +36,63 @@ const ModalMigrations = () => {
|
|||||||
return 'Important Update: Data Migration Needed'
|
return 'Important Update: Data Migration Needed'
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return loaderThreads || loaderModels
|
return threadAndMessageMigrationState === 'in_progress' ||
|
||||||
? 'Migration In Progress'
|
modelMigrationState === 'in_progress'
|
||||||
|
? 'Migrating'
|
||||||
: 'Migration Completed'
|
: 'Migration Completed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleStartMigration = async () => {
|
const migrationThreadsAndMessages = useCallback(async () => {
|
||||||
setStep(2)
|
setThreadAndMessageMigrationState('in_progress')
|
||||||
await handleStartMigrationModels()
|
|
||||||
await handleStartMigrationThreads()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleStartMigrationThreads = async () => {
|
|
||||||
setLoaderThreads(true)
|
|
||||||
await migrateThreadsAndMessages()
|
|
||||||
setTimeout(() => {
|
|
||||||
setLoaderThreads(false)
|
|
||||||
}, 1200)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleStartMigrationModels = async () => {
|
|
||||||
setLoaderModels(true)
|
|
||||||
await migrateModels()
|
|
||||||
setTimeout(() => {
|
|
||||||
setLoaderModels(false)
|
|
||||||
}, 1200)
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
getJanThreadsAndMessages,
|
|
||||||
migrateModels,
|
|
||||||
migrateThreadsAndMessages,
|
|
||||||
getJanLocalModels,
|
|
||||||
} = useMigratingData()
|
|
||||||
|
|
||||||
const getMigrationNotif = useCallback(async () => {
|
|
||||||
try {
|
try {
|
||||||
const resultThreadsAndMessages = await getJanThreadsAndMessages()
|
await migrateThreadsAndMessages()
|
||||||
const resultLocalModels = await getJanLocalModels()
|
setThreadAndMessageMigrationState('success')
|
||||||
if (
|
console.debug('Migrating threads and messages successfully!')
|
||||||
resultThreadsAndMessages.threads.length > 0 ||
|
} catch (err) {
|
||||||
resultThreadsAndMessages.messages.length > 0 ||
|
console.error('Migrating threads and messages error', err)
|
||||||
resultLocalModels
|
setThreadAndMessageMigrationState('failed')
|
||||||
) {
|
|
||||||
setDidShowMigrationWarning(true)
|
|
||||||
} else {
|
|
||||||
setDidShowMigrationWarning(false)
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}, [setThreadAndMessageMigrationState, migrateThreadsAndMessages])
|
||||||
setDidShowMigrationWarning(false)
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}, [getJanLocalModels, getJanThreadsAndMessages, setDidShowMigrationWarning])
|
|
||||||
|
|
||||||
useEffect(() => {
|
const migratingModels = useCallback(async () => {
|
||||||
if (
|
setModelMigrationState('in_progress')
|
||||||
skipMigration ||
|
try {
|
||||||
(threadsMessagesMigrationSuccess && modelsMigrationSuccess)
|
await migrateModels()
|
||||||
) {
|
setModelMigrationState('success')
|
||||||
return setDidShowMigrationWarning(false)
|
console.debug('Migrating models successfully!')
|
||||||
} else {
|
} catch (err) {
|
||||||
getMigrationNotif()
|
console.error('Migrating models error', err)
|
||||||
|
setModelMigrationState('failed')
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [migrateModels, setModelMigrationState])
|
||||||
}, [
|
|
||||||
skipMigration,
|
const onStartMigrationClick = useCallback(async () => {
|
||||||
setDidShowMigrationWarning,
|
setStep(2)
|
||||||
threadsMessagesMigrationSuccess,
|
await migratingModels()
|
||||||
modelsMigrationSuccess,
|
await migrationThreadsAndMessages()
|
||||||
])
|
}, [migratingModels, migrationThreadsAndMessages])
|
||||||
|
|
||||||
|
const onDismiss = useCallback(() => {
|
||||||
|
setStep(1)
|
||||||
|
setShowMigrationModal(false)
|
||||||
|
setDidShowMigrationModal(true)
|
||||||
|
}, [setDidShowMigrationModal, setShowMigrationModal])
|
||||||
|
|
||||||
|
const disableDismissButton = useMemo(
|
||||||
|
() =>
|
||||||
|
threadAndMessageMigrationState === 'in_progress' ||
|
||||||
|
modelMigrationState === 'in_progress',
|
||||||
|
[threadAndMessageMigrationState, modelMigrationState]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={didShowMigrationWarning}
|
open={showMigrationModal}
|
||||||
hideClose
|
hideClose
|
||||||
title={getStepTitle()}
|
title={getStepTitle()}
|
||||||
content={
|
content={
|
||||||
<>
|
<Fragment>
|
||||||
{step === 1 && (
|
{step === 1 && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<p className="text-[hsla(var(--text-secondary))]">
|
<p className="text-[hsla(var(--text-secondary))]">
|
||||||
@ -149,19 +127,10 @@ const ModalMigrations = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Button className="mt-4" theme="ghost" onClick={onDismiss}>
|
||||||
className="mt-4"
|
|
||||||
theme="ghost"
|
|
||||||
onClick={() => {
|
|
||||||
setSkipMigration(true)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Skip
|
Skip
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button className="ml-2 mt-4" onClick={onStartMigrationClick}>
|
||||||
className="ml-2 mt-4"
|
|
||||||
onClick={() => handleStartMigration()}
|
|
||||||
>
|
|
||||||
Migrate Now
|
Migrate Now
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -172,16 +141,17 @@ const ModalMigrations = () => {
|
|||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'mb-2 mt-4 flex justify-between rounded-lg border border-[hsla(var(--app-border))] p-3',
|
'mb-2 mt-4 flex justify-between rounded-lg border border-[hsla(var(--app-border))] p-3',
|
||||||
threadsMessagesMigrationSuccess
|
threadAndMessageMigrationState === 'success'
|
||||||
? 'bg-[hsla(var(--success-bg-soft))]'
|
? 'bg-[hsla(var(--success-bg-soft))]'
|
||||||
: 'bg-[hsla(var(--destructive-bg-soft))]',
|
: 'bg-[hsla(var(--destructive-bg-soft))]',
|
||||||
loaderThreads && 'bg-trasparent'
|
threadAndMessageMigrationState === 'in_progress' &&
|
||||||
|
'bg-trasparent'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-x-1.5">
|
<div className="flex items-center gap-x-1.5">
|
||||||
{!loaderThreads && (
|
{threadAndMessageMigrationState !== 'in_progress' && (
|
||||||
<>
|
<>
|
||||||
{threadsMessagesMigrationSuccess ? (
|
{threadAndMessageMigrationState === 'success' ? (
|
||||||
<Badge theme="success">Success</Badge>
|
<Badge theme="success">Success</Badge>
|
||||||
) : (
|
) : (
|
||||||
<Badge theme="destructive">Failed</Badge>
|
<Badge theme="destructive">Failed</Badge>
|
||||||
@ -190,14 +160,14 @@ const ModalMigrations = () => {
|
|||||||
)}
|
)}
|
||||||
<p className="font-bold">Threads</p>
|
<p className="font-bold">Threads</p>
|
||||||
</div>
|
</div>
|
||||||
{loaderThreads ? (
|
{threadAndMessageMigrationState === 'in_progress' ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
!threadsMessagesMigrationSuccess && (
|
threadAndMessageMigrationState !== 'success' && (
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
theme="ghost"
|
theme="ghost"
|
||||||
onClick={() => handleStartMigrationThreads()}
|
onClick={migrateThreadsAndMessages}
|
||||||
>
|
>
|
||||||
Retry
|
Retry
|
||||||
</Button>
|
</Button>
|
||||||
@ -207,16 +177,16 @@ const ModalMigrations = () => {
|
|||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
'my-2 flex justify-between rounded-lg border border-[hsla(var(--app-border))] p-3',
|
'my-2 flex justify-between rounded-lg border border-[hsla(var(--app-border))] p-3',
|
||||||
modelsMigrationSuccess
|
modelMigrationState === 'success'
|
||||||
? 'bg-[hsla(var(--success-bg-soft))]'
|
? 'bg-[hsla(var(--success-bg-soft))]'
|
||||||
: 'bg-[hsla(var(--destructive-bg-soft))]',
|
: 'bg-[hsla(var(--destructive-bg-soft))]',
|
||||||
loaderModels && 'bg-trasparent'
|
modelMigrationState === 'in_progress' && 'bg-trasparent'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-x-1.5">
|
<div className="flex items-center gap-x-1.5">
|
||||||
{!loaderModels && (
|
{modelMigrationState !== 'in_progress' && (
|
||||||
<>
|
<>
|
||||||
{modelsMigrationSuccess ? (
|
{modelMigrationState === 'success' ? (
|
||||||
<Badge theme="success">Success</Badge>
|
<Badge theme="success">Success</Badge>
|
||||||
) : (
|
) : (
|
||||||
<Badge theme="destructive">Failed</Badge>
|
<Badge theme="destructive">Failed</Badge>
|
||||||
@ -225,14 +195,14 @@ const ModalMigrations = () => {
|
|||||||
)}
|
)}
|
||||||
<p className="font-bold">Models</p>
|
<p className="font-bold">Models</p>
|
||||||
</div>
|
</div>
|
||||||
{loaderModels ? (
|
{modelMigrationState === 'in_progress' ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
!modelsMigrationSuccess && (
|
modelMigrationState === 'failed' && (
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
theme="ghost"
|
theme="ghost"
|
||||||
onClick={() => handleStartMigrationModels()}
|
onClick={migratingModels}
|
||||||
>
|
>
|
||||||
Retry
|
Retry
|
||||||
</Button>
|
</Button>
|
||||||
@ -242,17 +212,15 @@ const ModalMigrations = () => {
|
|||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
disabled={loaderThreads || loaderModels}
|
disabled={disableDismissButton}
|
||||||
onClick={() => {
|
onClick={onDismiss}
|
||||||
setDidShowMigrationWarning(false)
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Done
|
Done
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</>
|
</Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import ThemeWrapper from '@/containers/Providers/Theme'
|
|||||||
import { setupCoreServices } from '@/services/coreService'
|
import { setupCoreServices } from '@/services/coreService'
|
||||||
|
|
||||||
import DataLoader from './DataLoader'
|
import DataLoader from './DataLoader'
|
||||||
import ModalMigrations from './ModalMigrations'
|
|
||||||
|
|
||||||
import Responsive from './Responsive'
|
import Responsive from './Responsive'
|
||||||
|
|
||||||
@ -42,7 +41,6 @@ const Providers = ({ children }: PropsWithChildren) => {
|
|||||||
<Toaster />
|
<Toaster />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
<ModalMigrations />
|
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</JotaiWrapper>
|
</JotaiWrapper>
|
||||||
</ThemeWrapper>
|
</ThemeWrapper>
|
||||||
|
|||||||
@ -8,10 +8,6 @@ const IGNORE_SSL = 'ignoreSSLFeature'
|
|||||||
const HTTPS_PROXY_FEATURE = 'httpsProxyFeature'
|
const HTTPS_PROXY_FEATURE = 'httpsProxyFeature'
|
||||||
const QUICK_ASK_ENABLED = 'quickAskEnabled'
|
const QUICK_ASK_ENABLED = 'quickAskEnabled'
|
||||||
const MIGRATION_WARNING = 'didShowMigrationWarning'
|
const MIGRATION_WARNING = 'didShowMigrationWarning'
|
||||||
const THREADS_MESSAGES_MIGRATION_SUCCESS = 'threadsMessagesMigrationSuccess'
|
|
||||||
const MODELS_MIGRATION_SUCCESS = 'modelsMigrationSuccess'
|
|
||||||
const SKIP_MIGRATION = 'skipMigration'
|
|
||||||
|
|
||||||
export const janDataFolderPathAtom = atom('')
|
export const janDataFolderPathAtom = atom('')
|
||||||
|
|
||||||
export const experimentalFeatureEnabledAtom = atomWithStorage(
|
export const experimentalFeatureEnabledAtom = atomWithStorage(
|
||||||
@ -27,26 +23,6 @@ export const vulkanEnabledAtom = atomWithStorage(VULKAN_ENABLED, false)
|
|||||||
export const quickAskEnabledAtom = atomWithStorage(QUICK_ASK_ENABLED, false)
|
export const quickAskEnabledAtom = atomWithStorage(QUICK_ASK_ENABLED, false)
|
||||||
export const didShowMigrationWarningAtom = atomWithStorage(
|
export const didShowMigrationWarningAtom = atomWithStorage(
|
||||||
MIGRATION_WARNING,
|
MIGRATION_WARNING,
|
||||||
false
|
|
||||||
)
|
|
||||||
export const threadsMessagesMigrationSuccessAtom = atomWithStorage(
|
|
||||||
THREADS_MESSAGES_MIGRATION_SUCCESS,
|
|
||||||
false,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
getOnInit: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
export const modelsMigrationSuccessAtom = atomWithStorage(
|
|
||||||
MODELS_MIGRATION_SUCCESS,
|
|
||||||
false,
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
getOnInit: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
export const skipMigrationAtom = atomWithStorage(
|
|
||||||
SKIP_MIGRATION,
|
|
||||||
false,
|
false,
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
@ -54,4 +30,4 @@ export const skipMigrationAtom = atomWithStorage(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const hostAtom = atom('http://localhost:1337/v1')
|
export const hostAtom = atom('http://127.0.0.1:1338/v1')
|
||||||
|
|||||||
@ -247,9 +247,7 @@ const useCortex = () => {
|
|||||||
|
|
||||||
const downloadModel = useCallback(
|
const downloadModel = useCallback(
|
||||||
async (modelId: string, fileName?: string, persistedModelId?: string) => {
|
async (modelId: string, fileName?: string, persistedModelId?: string) => {
|
||||||
try {
|
const response = await fetch(`${host}/models/${modelId}/pull`, {
|
||||||
// return await cortex.models.download(modelId)
|
|
||||||
return await fetch(`${host}/models/${modelId}/pull`, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'accept': 'application/json',
|
'accept': 'application/json',
|
||||||
@ -261,8 +259,12 @@ const useCortex = () => {
|
|||||||
persistedModelId: persistedModelId,
|
persistedModelId: persistedModelId,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
} catch (err) {
|
if (!response.ok) {
|
||||||
console.error(err)
|
const responseJson = await response.json()
|
||||||
|
const errorMessage: string =
|
||||||
|
(responseJson.error?.message as string) ??
|
||||||
|
`Failed to download model ${modelId}`
|
||||||
|
throw new Error(errorMessage)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[host]
|
[host]
|
||||||
|
|||||||
@ -1,25 +1,13 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
import { useAtom } from 'jotai'
|
|
||||||
|
|
||||||
import useAssistantQuery from './useAssistantQuery'
|
import useAssistantQuery from './useAssistantQuery'
|
||||||
|
|
||||||
import useCortex from './useCortex'
|
import useCortex from './useCortex'
|
||||||
import useMessageCreateMutation from './useMessageCreateMutation'
|
import useMessageCreateMutation from './useMessageCreateMutation'
|
||||||
import useThreads from './useThreads'
|
import useThreads from './useThreads'
|
||||||
|
|
||||||
import {
|
|
||||||
threadsMessagesMigrationSuccessAtom,
|
|
||||||
modelsMigrationSuccessAtom,
|
|
||||||
} from '@/helpers/atoms/AppConfig.atom'
|
|
||||||
|
|
||||||
const useMigratingData = () => {
|
const useMigratingData = () => {
|
||||||
const [threadsMessagesMigrationSuccess, setThreadsMessagesMigrationSuccess] =
|
|
||||||
useAtom(threadsMessagesMigrationSuccessAtom)
|
|
||||||
const [modelsMigrationSuccess, setModelsMigrationSuccess] = useAtom(
|
|
||||||
modelsMigrationSuccessAtom
|
|
||||||
)
|
|
||||||
const { createThread } = useThreads()
|
const { createThread } = useThreads()
|
||||||
const { updateThread } = useCortex()
|
const { updateThread } = useCortex()
|
||||||
const createMessage = useMessageCreateMutation()
|
const createMessage = useMessageCreateMutation()
|
||||||
@ -32,45 +20,34 @@ const useMigratingData = () => {
|
|||||||
return window?.electronAPI?.getAllMessagesAndThreads()
|
return window?.electronAPI?.getAllMessagesAndThreads()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getJanLocalModels = useCallback(async (): Promise<{
|
const getJanLocalModels = useCallback(async (): Promise<boolean> => {
|
||||||
hasLocalModels: boolean
|
// TODO: change the name of this function
|
||||||
}> => {
|
|
||||||
return window?.electronAPI?.getAllLocalModels()
|
return window?.electronAPI?.getAllLocalModels()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const migrateModels = useCallback(async () => {
|
const migrateModels = useCallback(async () => {
|
||||||
try {
|
return window?.electronAPI?.syncModelFileToCortex()
|
||||||
if (!modelsMigrationSuccess) {
|
}, [])
|
||||||
await window?.electronAPI?.syncModelFileToCortex()
|
|
||||||
setModelsMigrationSuccess(true)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
setModelsMigrationSuccess(false)
|
|
||||||
}
|
|
||||||
}, [modelsMigrationSuccess, setModelsMigrationSuccess])
|
|
||||||
|
|
||||||
const migrateThreadsAndMessages = useCallback(async () => {
|
const migrateThreadsAndMessages = useCallback(async () => {
|
||||||
if (!assistants || assistants.length === 0) {
|
if (!assistants || assistants.length === 0) {
|
||||||
console.error('No assistant found')
|
console.error('No assistant found')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
if (threadsMessagesMigrationSuccess) return
|
|
||||||
const threadsAndMessages = await getJanThreadsAndMessages()
|
const threadsAndMessages = await getJanThreadsAndMessages()
|
||||||
const janThreads = threadsAndMessages.threads
|
const janThreads = threadsAndMessages.threads
|
||||||
|
|
||||||
for (const thread of janThreads) {
|
for (const thread of janThreads) {
|
||||||
const modelId: string | undefined = thread.assistants[0]?.model?.id
|
const modelId: string | undefined = thread.assistants[0]?.model?.id
|
||||||
if (!modelId || modelId.trim().length === 0 || modelId === '*') {
|
if (!modelId || modelId.trim().length === 0 || modelId === '*') {
|
||||||
console.error(
|
console.error(`Ignore thread ${thread.id} because modelId is not found`)
|
||||||
`Ignore thread ${thread.id} because modelId is not found`
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const threadTitle: string = thread.title ?? 'New Thread'
|
const threadTitle: string = thread.title ?? 'New Thread'
|
||||||
const instructions: string = thread.assistants[0]?.instructions ?? ''
|
const instructions: string = thread.assistants[0]?.instructions ?? ''
|
||||||
// currently, we don't have api support for creating thread with messages
|
// currently, we don't have api support for creating thread with messages
|
||||||
const cortexThread = await createThread(modelId, assistants[0])
|
const cortexThread = await createThread(modelId, assistants[0])
|
||||||
|
|
||||||
console.log('createThread', cortexThread)
|
console.log('createThread', cortexThread)
|
||||||
// update instruction
|
// update instruction
|
||||||
cortexThread.assistants[0].instructions = instructions
|
cortexThread.assistants[0].instructions = instructions
|
||||||
@ -84,14 +61,12 @@ const useMigratingData = () => {
|
|||||||
const janMessages = threadsAndMessages.messages.filter(
|
const janMessages = threadsAndMessages.messages.filter(
|
||||||
(m) => m.thread_id === thread.id
|
(m) => m.thread_id === thread.id
|
||||||
)
|
)
|
||||||
console.log(janMessages)
|
|
||||||
for (let j = 0; j < janMessages.length; ++j) {
|
for (let j = 0; j < janMessages.length; ++j) {
|
||||||
const janMessage = janMessages[j]
|
const janMessage = janMessages[j]
|
||||||
// filter out the system message if any
|
// filter out the system message if any
|
||||||
if (janMessage.role === 'system') continue
|
if (janMessage.role === 'system') continue
|
||||||
try {
|
const messageContent: string = janMessage.content[0]?.text.value ?? ''
|
||||||
const messageContent: string =
|
|
||||||
janMessage.content[0]?.text.value ?? ''
|
|
||||||
|
|
||||||
// can speed up here with Promise.allSettled
|
// can speed up here with Promise.allSettled
|
||||||
await createMessage.mutateAsync({
|
await createMessage.mutateAsync({
|
||||||
@ -101,23 +76,13 @@ const useMigratingData = () => {
|
|||||||
role: janMessage.role,
|
role: janMessage.role,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setThreadsMessagesMigrationSuccess(true)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
setThreadsMessagesMigrationSuccess(false)
|
|
||||||
}
|
|
||||||
}, [
|
}, [
|
||||||
assistants,
|
assistants,
|
||||||
getJanThreadsAndMessages,
|
getJanThreadsAndMessages,
|
||||||
threadsMessagesMigrationSuccess,
|
|
||||||
createThread,
|
createThread,
|
||||||
updateThread,
|
updateThread,
|
||||||
setThreadsMessagesMigrationSuccess,
|
|
||||||
createMessage,
|
createMessage,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,64 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
|
import { Button } from '@janhq/joi'
|
||||||
|
import { useAtomValue, useSetAtom } from 'jotai'
|
||||||
|
|
||||||
|
import { showMigrationModalAtom } from '@/containers/Providers/ModalMigrations'
|
||||||
|
|
||||||
|
import { toaster } from '@/containers/Toast'
|
||||||
|
|
||||||
|
import useThreads from '@/hooks/useThreads'
|
||||||
|
|
||||||
|
import { threadsAtom } from '@/helpers/atoms/Thread.atom'
|
||||||
|
|
||||||
|
const DataMigration: React.FC = () => {
|
||||||
|
const setShowMigrationModal = useSetAtom(showMigrationModalAtom)
|
||||||
|
const threads = useAtomValue(threadsAtom)
|
||||||
|
const { deleteThread } = useThreads()
|
||||||
|
|
||||||
|
const onStartMigrationClick = useCallback(() => {
|
||||||
|
setShowMigrationModal(true)
|
||||||
|
}, [setShowMigrationModal])
|
||||||
|
|
||||||
|
const onCleanUpDataClick = useCallback(async () => {
|
||||||
|
for (const thread of threads) {
|
||||||
|
try {
|
||||||
|
await deleteThread(thread.id)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting thread', err)
|
||||||
|
toaster({
|
||||||
|
title: 'Delete thread failed',
|
||||||
|
description: `Failed to delete thread ${thread.title}`,
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toaster({
|
||||||
|
title: 'Delete thread successfully!',
|
||||||
|
type: 'success',
|
||||||
|
})
|
||||||
|
}, [threads, deleteThread])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-col items-start justify-between gap-4 border-b border-[hsla(var(--app-border))] py-4 first:pt-0 last:border-none sm:flex-row">
|
||||||
|
<div className="flex-shrink-0 space-y-1">
|
||||||
|
<div className="flex gap-x-2">
|
||||||
|
<h6 className="font-semibold capitalize">Clear logs</h6>
|
||||||
|
</div>
|
||||||
|
<p className="font-medium leading-relaxed text-[hsla(var(--text-secondary))]">
|
||||||
|
Clear all logs from Jan app.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-x-2">
|
||||||
|
<Button theme="primary" onClick={onStartMigrationClick}>
|
||||||
|
Start migration
|
||||||
|
</Button>
|
||||||
|
<Button theme="destructive" onClick={onCleanUpDataClick}>
|
||||||
|
Remove threads and messages
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataMigration
|
||||||
@ -13,6 +13,8 @@ import { toaster } from '@/containers/Toast'
|
|||||||
import useModelStop from '@/hooks/useModelStop'
|
import useModelStop from '@/hooks/useModelStop'
|
||||||
import { useSettings } from '@/hooks/useSettings'
|
import { useSettings } from '@/hooks/useSettings'
|
||||||
|
|
||||||
|
import DataMigration from './FactoryReset/components/DataMigration'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
experimentalFeatureEnabledAtom,
|
experimentalFeatureEnabledAtom,
|
||||||
ignoreSslAtom,
|
ignoreSslAtom,
|
||||||
@ -21,6 +23,7 @@ import {
|
|||||||
vulkanEnabledAtom,
|
vulkanEnabledAtom,
|
||||||
quickAskEnabledAtom,
|
quickAskEnabledAtom,
|
||||||
} from '@/helpers/atoms/AppConfig.atom'
|
} from '@/helpers/atoms/AppConfig.atom'
|
||||||
|
|
||||||
import { activeModelsAtom } from '@/helpers/atoms/Model.atom'
|
import { activeModelsAtom } from '@/helpers/atoms/Model.atom'
|
||||||
|
|
||||||
// type GPU = {
|
// type GPU = {
|
||||||
@ -459,6 +462,7 @@ const Advanced = () => {
|
|||||||
|
|
||||||
{/* Factory Reset */}
|
{/* Factory Reset */}
|
||||||
{/* <FactoryReset /> */}
|
{/* <FactoryReset /> */}
|
||||||
|
{experimentalEnabled && <DataMigration />}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from 'react'
|
import { Fragment, useEffect } from 'react'
|
||||||
|
|
||||||
import { Modal } from '@janhq/joi'
|
import { Modal } from '@janhq/joi'
|
||||||
import { useAtomValue, useSetAtom } from 'jotai'
|
import { useAtomValue, useSetAtom } from 'jotai'
|
||||||
@ -13,11 +13,15 @@ import {
|
|||||||
|
|
||||||
import ImportingModelItem from './ImportingModelItem'
|
import ImportingModelItem from './ImportingModelItem'
|
||||||
|
|
||||||
import { importingModelsAtom } from '@/helpers/atoms/Model.atom'
|
import {
|
||||||
|
importingModelsAtom,
|
||||||
|
setImportingModelErrorAtom,
|
||||||
|
} from '@/helpers/atoms/Model.atom'
|
||||||
|
|
||||||
const ImportingModelModal = () => {
|
const ImportingModelModal = () => {
|
||||||
const { downloadModel } = useCortex()
|
const { downloadModel } = useCortex()
|
||||||
const setImportModelStage = useSetAtom(setImportModelStageAtom)
|
const setImportModelStage = useSetAtom(setImportModelStageAtom)
|
||||||
|
const setImportModelError = useSetAtom(setImportingModelErrorAtom)
|
||||||
const importingModels = useAtomValue(importingModelsAtom)
|
const importingModels = useAtomValue(importingModelsAtom)
|
||||||
const importModelStage = useAtomValue(getImportModelStageAtom)
|
const importModelStage = useAtomValue(getImportModelStageAtom)
|
||||||
|
|
||||||
@ -28,7 +32,16 @@ const ImportingModelModal = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const importModels = async () => {
|
const importModels = async () => {
|
||||||
for (const model of importingModels) {
|
for (const model of importingModels) {
|
||||||
|
try {
|
||||||
await downloadModel(model.path)
|
await downloadModel(model.path)
|
||||||
|
} catch (error) {
|
||||||
|
let errorMessage = String(error)
|
||||||
|
if (error instanceof Error) {
|
||||||
|
errorMessage = error.message
|
||||||
|
}
|
||||||
|
|
||||||
|
setImportModelError(model.path, errorMessage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
importModels()
|
importModels()
|
||||||
@ -41,21 +54,7 @@ const ImportingModelModal = () => {
|
|||||||
onOpenChange={() => setImportModelStage('NONE')}
|
onOpenChange={() => setImportModelStage('NONE')}
|
||||||
title={`Importing model (${finishedImportModel}/${importingModels.length})`}
|
title={`Importing model (${finishedImportModel}/${importingModels.length})`}
|
||||||
content={
|
content={
|
||||||
<div>
|
<Fragment>
|
||||||
<div className="flex flex-row items-center space-x-2 pb-3">
|
|
||||||
{/* <label className="text-[hsla(var(--text-secondary)] text-xs">
|
|
||||||
{modelFolder}
|
|
||||||
</label>
|
|
||||||
<Button
|
|
||||||
theme="ghost"
|
|
||||||
size="small"
|
|
||||||
variant="outline"
|
|
||||||
onClick={onOpenModelFolderClick}
|
|
||||||
>
|
|
||||||
{openFileTitle()}
|
|
||||||
</Button> */}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-2 space-y-3">
|
<div className="mb-2 space-y-3">
|
||||||
{importingModels.map((model) => (
|
{importingModels.map((model) => (
|
||||||
<ImportingModelItem key={model.importId} model={model} />
|
<ImportingModelItem key={model.importId} model={model} />
|
||||||
@ -75,7 +74,7 @@ const ImportingModelModal = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user