enhancement: update ui dialog update llamacpp backend
This commit is contained in:
parent
a6e4f28830
commit
ba4dc6d1eb
@ -28,6 +28,13 @@ export async function getLocalInstalledBackends(): Promise<
|
||||
for (const version of versionDirs) {
|
||||
const versionPath = await joinPath([backendsDir, version])
|
||||
const versionName = await basename(versionPath)
|
||||
|
||||
// Check if versionPath is actually a directory before reading it
|
||||
const versionStat = await fs.fileStat(versionPath)
|
||||
if (!versionStat?.isDirectory) {
|
||||
continue
|
||||
}
|
||||
|
||||
const backendTypes = await fs.readdirSync(versionPath)
|
||||
|
||||
// Verify that the backend is really installed
|
||||
@ -150,8 +157,9 @@ export async function listSupportedBackends(): Promise<
|
||||
supportedBackends.push('macos-arm64')
|
||||
}
|
||||
// get latest backends from Github
|
||||
const remoteBackendVersions =
|
||||
await fetchRemoteSupportedBackends(supportedBackends)
|
||||
const remoteBackendVersions = await fetchRemoteSupportedBackends(
|
||||
supportedBackends
|
||||
)
|
||||
|
||||
// Get locally installed versions
|
||||
const localBackendVersions = await getLocalInstalledBackends()
|
||||
|
||||
104
web-app/src/containers/dialogs/BackendUpdater.tsx
Normal file
104
web-app/src/containers/dialogs/BackendUpdater.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import { useBackendUpdater } from '@/hooks/useBackendUpdater'
|
||||
|
||||
import { IconDownload } from '@tabler/icons-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
const BackendUpdater = () => {
|
||||
const { t } = useTranslation()
|
||||
const { updateState, updateBackend, checkForUpdate, setRemindMeLater } =
|
||||
useBackendUpdater()
|
||||
|
||||
const handleUpdate = async () => {
|
||||
try {
|
||||
await updateBackend()
|
||||
setRemindMeLater(true)
|
||||
toast.success(t('settings:backendUpdater.updateSuccess'))
|
||||
} catch (error) {
|
||||
console.error('Backend update failed:', error)
|
||||
toast.error(t('settings:backendUpdater.updateError'))
|
||||
}
|
||||
}
|
||||
|
||||
// Check for updates when component mounts
|
||||
useEffect(() => {
|
||||
checkForUpdate()
|
||||
}, [checkForUpdate])
|
||||
|
||||
const [backendUpdateState, setBackendUpdateState] = useState({
|
||||
remindMeLater: false,
|
||||
isUpdateAvailable: false,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setBackendUpdateState({
|
||||
remindMeLater: updateState.remindMeLater,
|
||||
isUpdateAvailable: updateState.isUpdateAvailable,
|
||||
})
|
||||
}, [updateState])
|
||||
|
||||
// Don't show if user clicked remind me later or auto update is enabled
|
||||
if (backendUpdateState.remindMeLater || updateState.autoUpdateEnabled)
|
||||
return null
|
||||
|
||||
return (
|
||||
<>
|
||||
{backendUpdateState.isUpdateAvailable && (
|
||||
<div
|
||||
className={cn(
|
||||
'fixed z-50 min-w-[300px] bottom-3 right-3 bg-main-view text-main-view-fg flex items-center justify-center border border-main-view-fg/10 rounded-lg shadow-md'
|
||||
)}
|
||||
>
|
||||
<div className="px-0 py-4">
|
||||
<div className="px-4">
|
||||
<div className="flex items-start gap-2">
|
||||
<IconDownload
|
||||
size={20}
|
||||
className="shrink-0 text-main-view-fg/60 mt-1"
|
||||
/>
|
||||
<div>
|
||||
<div className="text-base font-medium">
|
||||
{t('settings:backendUpdater.newBackendVersion', {
|
||||
version: updateState.updateInfo?.newVersion,
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-1 text-main-view-fg/70 font-normal mb-2">
|
||||
{t('settings:backendUpdater.backendUpdateAvailable')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-3 px-4">
|
||||
<div className="flex gap-x-4 w-full items-center justify-end">
|
||||
<div className="flex gap-x-5">
|
||||
<Button
|
||||
variant="link"
|
||||
className="px-0 text-main-view-fg/70 remind-me-later"
|
||||
onClick={() => setRemindMeLater(true)}
|
||||
>
|
||||
{t('settings:backendUpdater.remindMeLater')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleUpdate}
|
||||
disabled={updateState.isUpdating}
|
||||
>
|
||||
{updateState.isUpdating
|
||||
? t('settings:backendUpdater.updating')
|
||||
: t('settings:backendUpdater.updateNow')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BackendUpdater
|
||||
348
web-app/src/hooks/useBackendUpdater.ts
Normal file
348
web-app/src/hooks/useBackendUpdater.ts
Normal file
@ -0,0 +1,348 @@
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
import { events } from '@janhq/core'
|
||||
import { ExtensionManager } from '@/lib/extension'
|
||||
|
||||
export interface BackendUpdateInfo {
|
||||
updateNeeded: boolean
|
||||
newVersion: string
|
||||
currentVersion?: string
|
||||
}
|
||||
|
||||
interface ExtensionSetting {
|
||||
key: string
|
||||
controllerProps?: {
|
||||
value: unknown
|
||||
}
|
||||
}
|
||||
|
||||
interface LlamacppExtension {
|
||||
getSettings?(): Promise<ExtensionSetting[]>
|
||||
checkBackendForUpdates?(): Promise<BackendUpdateInfo>
|
||||
updateBackend?(
|
||||
targetBackend: string
|
||||
): Promise<{ wasUpdated: boolean; newBackend: string }>
|
||||
installBackend?(filePath: string): Promise<void>
|
||||
}
|
||||
|
||||
export interface BackendUpdateState {
|
||||
isUpdateAvailable: boolean
|
||||
updateInfo: BackendUpdateInfo | null
|
||||
isUpdating: boolean
|
||||
remindMeLater: boolean
|
||||
autoUpdateEnabled: boolean
|
||||
}
|
||||
|
||||
export const useBackendUpdater = () => {
|
||||
const [updateState, setUpdateState] = useState<BackendUpdateState>({
|
||||
isUpdateAvailable: false,
|
||||
updateInfo: null,
|
||||
isUpdating: false,
|
||||
remindMeLater: false,
|
||||
autoUpdateEnabled: false,
|
||||
})
|
||||
|
||||
// Listen for backend update state sync events
|
||||
useEffect(() => {
|
||||
const handleUpdateStateSync = (newState: Partial<BackendUpdateState>) => {
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
...newState,
|
||||
}))
|
||||
}
|
||||
|
||||
events.on('onBackendUpdateStateSync', handleUpdateStateSync)
|
||||
|
||||
return () => {
|
||||
events.off('onBackendUpdateStateSync', handleUpdateStateSync)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Check auto update setting from llamacpp extension
|
||||
useEffect(() => {
|
||||
const checkAutoUpdateSetting = async () => {
|
||||
try {
|
||||
// Get llamacpp extension instance
|
||||
const allExtensions = ExtensionManager.getInstance().listExtensions()
|
||||
let llamacppExtension =
|
||||
ExtensionManager.getInstance().getByName('llamacpp-extension')
|
||||
|
||||
if (!llamacppExtension) {
|
||||
// Try to find by type or other properties
|
||||
llamacppExtension =
|
||||
allExtensions.find(
|
||||
(ext) =>
|
||||
ext.constructor.name.toLowerCase().includes('llamacpp') ||
|
||||
(ext.type &&
|
||||
ext.type()?.toString().toLowerCase().includes('inference'))
|
||||
) || undefined
|
||||
}
|
||||
|
||||
if (llamacppExtension && 'getSettings' in llamacppExtension) {
|
||||
const extension = llamacppExtension as LlamacppExtension
|
||||
const settings = await extension.getSettings?.()
|
||||
const autoUpdateSetting = settings?.find(
|
||||
(s) => s.key === 'auto_update_engine'
|
||||
)
|
||||
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
autoUpdateEnabled:
|
||||
autoUpdateSetting?.controllerProps?.value === true,
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check auto update setting:', error)
|
||||
}
|
||||
}
|
||||
|
||||
checkAutoUpdateSetting()
|
||||
}, [])
|
||||
|
||||
const syncStateToOtherInstances = useCallback(
|
||||
(partialState: Partial<BackendUpdateState>) => {
|
||||
// Emit event to sync state across all useBackendUpdater instances
|
||||
events.emit('onBackendUpdateStateSync', partialState)
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const checkForUpdate = useCallback(
|
||||
async (resetRemindMeLater = false) => {
|
||||
try {
|
||||
// Reset remindMeLater if requested (e.g., when called from settings)
|
||||
if (resetRemindMeLater) {
|
||||
const newState = {
|
||||
remindMeLater: false,
|
||||
}
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
...newState,
|
||||
}))
|
||||
syncStateToOtherInstances(newState)
|
||||
}
|
||||
|
||||
// Get llamacpp extension instance
|
||||
const allExtensions = ExtensionManager.getInstance().listExtensions()
|
||||
|
||||
const llamacppExtension =
|
||||
ExtensionManager.getInstance().getByName('llamacpp-extension')
|
||||
|
||||
let extensionToUse = llamacppExtension
|
||||
|
||||
if (!llamacppExtension) {
|
||||
// Try to find by type or other properties
|
||||
const possibleExtension = allExtensions.find(
|
||||
(ext) =>
|
||||
ext.constructor.name.toLowerCase().includes('llamacpp') ||
|
||||
(ext.type &&
|
||||
ext.type()?.toString().toLowerCase().includes('inference'))
|
||||
)
|
||||
|
||||
if (!possibleExtension) {
|
||||
console.error('LlamaCpp extension not found')
|
||||
return null
|
||||
}
|
||||
|
||||
extensionToUse = possibleExtension
|
||||
}
|
||||
|
||||
if (!extensionToUse || !('checkBackendForUpdates' in extensionToUse)) {
|
||||
console.error(
|
||||
'Extension does not support checkBackendForUpdates method'
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
// Call the extension's checkBackendForUpdates method
|
||||
const extension = extensionToUse as LlamacppExtension
|
||||
const updateInfo = await extension.checkBackendForUpdates?.()
|
||||
|
||||
if (updateInfo?.updateNeeded) {
|
||||
const newState = {
|
||||
isUpdateAvailable: true,
|
||||
remindMeLater: false,
|
||||
updateInfo,
|
||||
}
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
...newState,
|
||||
}))
|
||||
syncStateToOtherInstances(newState)
|
||||
console.log('Backend update available:', updateInfo?.newVersion)
|
||||
return updateInfo
|
||||
} else {
|
||||
// No update available - reset state
|
||||
const newState = {
|
||||
isUpdateAvailable: false,
|
||||
updateInfo: null,
|
||||
}
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
...newState,
|
||||
}))
|
||||
syncStateToOtherInstances(newState)
|
||||
return null
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking for backend updates:', error)
|
||||
// Reset state on error
|
||||
const newState = {
|
||||
isUpdateAvailable: false,
|
||||
updateInfo: null,
|
||||
}
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
...newState,
|
||||
}))
|
||||
syncStateToOtherInstances(newState)
|
||||
return null
|
||||
}
|
||||
},
|
||||
[syncStateToOtherInstances]
|
||||
)
|
||||
|
||||
const setRemindMeLater = useCallback(
|
||||
(remind: boolean) => {
|
||||
const newState = {
|
||||
remindMeLater: remind,
|
||||
}
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
...newState,
|
||||
}))
|
||||
syncStateToOtherInstances(newState)
|
||||
},
|
||||
[syncStateToOtherInstances]
|
||||
)
|
||||
|
||||
const updateBackend = useCallback(async () => {
|
||||
if (!updateState.updateInfo) return
|
||||
|
||||
try {
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
isUpdating: true,
|
||||
}))
|
||||
|
||||
// Get llamacpp extension instance
|
||||
const allExtensions = ExtensionManager.getInstance().listExtensions()
|
||||
const llamacppExtension =
|
||||
ExtensionManager.getInstance().getByName('llamacpp-extension')
|
||||
|
||||
let extensionToUse = llamacppExtension
|
||||
|
||||
if (!llamacppExtension) {
|
||||
// Try to find by type or other properties
|
||||
const possibleExtension = allExtensions.find(
|
||||
(ext) =>
|
||||
ext.constructor.name.toLowerCase().includes('llamacpp') ||
|
||||
(ext.type &&
|
||||
ext.type()?.toString().toLowerCase().includes('inference'))
|
||||
)
|
||||
|
||||
if (!possibleExtension) {
|
||||
throw new Error('LlamaCpp extension not found')
|
||||
}
|
||||
|
||||
extensionToUse = possibleExtension
|
||||
}
|
||||
|
||||
if (
|
||||
!extensionToUse ||
|
||||
!('getSettings' in extensionToUse) ||
|
||||
!('updateBackend' in extensionToUse)
|
||||
) {
|
||||
throw new Error('Extension does not support backend updates')
|
||||
}
|
||||
|
||||
// Get current backend version to construct target backend string
|
||||
const extension = extensionToUse as LlamacppExtension
|
||||
const settings = await extension.getSettings?.()
|
||||
const currentBackendSetting = settings?.find(
|
||||
(s) => s.key === 'version_backend'
|
||||
)
|
||||
const currentBackend = currentBackendSetting?.controllerProps
|
||||
?.value as string
|
||||
|
||||
if (!currentBackend) {
|
||||
throw new Error('Current backend not found')
|
||||
}
|
||||
|
||||
// Extract backend type from current backend string (e.g., "b3224/cuda12" -> "cuda12")
|
||||
const [, backendType] = currentBackend.split('/')
|
||||
const targetBackendString = `${updateState.updateInfo.newVersion}/${backendType}`
|
||||
|
||||
// Call the extension's updateBackend method
|
||||
const result = await extension.updateBackend?.(targetBackendString)
|
||||
|
||||
if (result?.wasUpdated) {
|
||||
// Reset update state
|
||||
const newState = {
|
||||
isUpdateAvailable: false,
|
||||
updateInfo: null,
|
||||
isUpdating: false,
|
||||
}
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
...newState,
|
||||
}))
|
||||
syncStateToOtherInstances(newState)
|
||||
} else {
|
||||
throw new Error('Backend update failed')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating backend:', error)
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
isUpdating: false,
|
||||
}))
|
||||
throw error
|
||||
}
|
||||
}, [updateState.updateInfo, syncStateToOtherInstances])
|
||||
|
||||
const installBackend = useCallback(async (filePath: string) => {
|
||||
try {
|
||||
// Get llamacpp extension instance
|
||||
const allExtensions = ExtensionManager.getInstance().listExtensions()
|
||||
const llamacppExtension =
|
||||
ExtensionManager.getInstance().getByName('llamacpp-extension')
|
||||
|
||||
let extensionToUse = llamacppExtension
|
||||
|
||||
if (!llamacppExtension) {
|
||||
// Try to find by type or other properties
|
||||
const possibleExtension = allExtensions.find(
|
||||
(ext) =>
|
||||
ext.constructor.name.toLowerCase().includes('llamacpp') ||
|
||||
(ext.type &&
|
||||
ext.type()?.toString().toLowerCase().includes('inference'))
|
||||
)
|
||||
|
||||
if (!possibleExtension) {
|
||||
throw new Error('LlamaCpp extension not found')
|
||||
}
|
||||
|
||||
extensionToUse = possibleExtension
|
||||
}
|
||||
|
||||
if (!extensionToUse || !('installBackend' in extensionToUse)) {
|
||||
throw new Error('Extension does not support backend installation')
|
||||
}
|
||||
|
||||
// Call the extension's installBackend method
|
||||
const extension = extensionToUse as LlamacppExtension
|
||||
await extension.installBackend?.(filePath)
|
||||
} catch (error) {
|
||||
console.error('Error installing backend:', error)
|
||||
throw error
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
updateState,
|
||||
checkForUpdate,
|
||||
updateBackend,
|
||||
setRemindMeLater,
|
||||
installBackend,
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,11 @@
|
||||
"noUpdateAvailable": "Du verwendest die neueste Version",
|
||||
"devVersion": "Entwicklungsversion erkannt",
|
||||
"updateError": "Fehler beim Suchen nach Updates",
|
||||
"checkForBackendUpdates": "LlamaCpp Updates prüfen",
|
||||
"checkForBackendUpdatesDesc": "Prüfe, ob eine neuere Version des LlamaCpp-Backends verfügbar ist.",
|
||||
"checkingForBackendUpdates": "Suche nach LlamaCpp Updates...",
|
||||
"noBackendUpdateAvailable": "Du verwendest die neueste LlamaCpp Version",
|
||||
"backendUpdateError": "Fehler beim Suchen nach LlamaCpp Updates",
|
||||
"changeLocation": "Ort ändern",
|
||||
"copied": "Kopiert",
|
||||
"copyPath": "Pfad kopieren",
|
||||
@ -244,5 +249,14 @@
|
||||
"cancel": "Abbrechen",
|
||||
"changeLocation": "Ort ändern"
|
||||
}
|
||||
},
|
||||
"backendUpdater": {
|
||||
"newBackendVersion": "Neue LlamaCpp Version {{version}}",
|
||||
"backendUpdateAvailable": "LlamaCpp Update verfügbar",
|
||||
"remindMeLater": "Später erinnern",
|
||||
"updating": "Aktualisiere...",
|
||||
"updateNow": "Jetzt aktualisieren",
|
||||
"updateSuccess": "LlamaCpp erfolgreich aktualisiert",
|
||||
"updateError": "Fehler beim Aktualisieren von LlamaCpp"
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,11 @@
|
||||
"noUpdateAvailable": "You're running the latest version",
|
||||
"devVersion": "Development version detected",
|
||||
"updateError": "Failed to check for updates",
|
||||
"checkForBackendUpdates": "Check for LlamaCpp Updates",
|
||||
"checkForBackendUpdatesDesc": "Check if a newer version of the LlamaCpp backend is available.",
|
||||
"checkingForBackendUpdates": "Checking for LlamaCpp updates...",
|
||||
"noBackendUpdateAvailable": "You're running the latest LlamaCpp version",
|
||||
"backendUpdateError": "Failed to check for LlamaCpp updates",
|
||||
"changeLocation": "Change Location",
|
||||
"copied": "Copied",
|
||||
"copyPath": "Copy Path",
|
||||
@ -249,5 +254,16 @@
|
||||
"cancel": "Cancel",
|
||||
"changeLocation": "Change Location"
|
||||
}
|
||||
}
|
||||
},
|
||||
"backendUpdater": {
|
||||
"newBackendVersion": "New LlamaCpp Version {{version}}",
|
||||
"backendUpdateAvailable": "LlamaCpp Update Available",
|
||||
"remindMeLater": "Remind Me Later",
|
||||
"updating": "Updating...",
|
||||
"updateNow": "Update Now",
|
||||
"updateSuccess": "LlamaCpp updated successfully",
|
||||
"updateError": "Failed to update LlamaCpp"
|
||||
},
|
||||
"backendInstallSuccess": "Backend installed successfully",
|
||||
"backendInstallError": "Failed to install backend"
|
||||
}
|
||||
|
||||
@ -6,6 +6,11 @@
|
||||
"noUpdateAvailable": "Anda menjalankan versi terbaru",
|
||||
"devVersion": "Versi pengembangan terdeteksi",
|
||||
"updateError": "Gagal memeriksa pembaruan",
|
||||
"checkForBackendUpdates": "Periksa Pembaruan LlamaCpp",
|
||||
"checkForBackendUpdatesDesc": "Periksa apakah versi backend LlamaCpp yang lebih baru tersedia.",
|
||||
"checkingForBackendUpdates": "Memeriksa pembaruan LlamaCpp...",
|
||||
"noBackendUpdateAvailable": "Anda menjalankan versi LlamaCpp terbaru",
|
||||
"backendUpdateError": "Gagal memeriksa pembaruan LlamaCpp",
|
||||
"changeLocation": "Ubah Lokasi",
|
||||
"copied": "Tersalin",
|
||||
"copyPath": "Salin Jalur",
|
||||
@ -244,5 +249,14 @@
|
||||
"cancel": "Batal",
|
||||
"changeLocation": "Ubah Lokasi"
|
||||
}
|
||||
},
|
||||
"backendUpdater": {
|
||||
"newBackendVersion": "Versi LlamaCpp Baru {{version}}",
|
||||
"backendUpdateAvailable": "Pembaruan LlamaCpp Tersedia",
|
||||
"remindMeLater": "Ingatkan Saya Nanti",
|
||||
"updating": "Memperbarui...",
|
||||
"updateNow": "Perbarui Sekarang",
|
||||
"updateSuccess": "LlamaCpp berhasil diperbarui",
|
||||
"updateError": "Gagal memperbarui LlamaCpp"
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,11 @@
|
||||
"noUpdateAvailable": "Używasz najnowszej wersji",
|
||||
"devVersion": "Wykryto wersję deweloperską",
|
||||
"updateError": "Nie udało się sprawdzić dostępności aktualizacji",
|
||||
"checkForBackendUpdates": "Sprawdź Aktualizacje LlamaCpp",
|
||||
"checkForBackendUpdatesDesc": "Sprawdza czy dostępna jest nowa wersja backendu LlamaCpp.",
|
||||
"checkingForBackendUpdates": "Sprawdzanie aktualizacji LlamaCpp...",
|
||||
"noBackendUpdateAvailable": "Używasz najnowszej wersji LlamaCpp",
|
||||
"backendUpdateError": "Nie udało się sprawdzić aktualizacji LlamaCpp",
|
||||
"changeLocation": "Zmień Położenie",
|
||||
"copied": "Skopiowano",
|
||||
"copyPath": "Skopiuj Ścieżkę",
|
||||
@ -249,5 +254,14 @@
|
||||
"cancel": "Anuluj",
|
||||
"changeLocation": "Zmień Położenie"
|
||||
}
|
||||
},
|
||||
"backendUpdater": {
|
||||
"newBackendVersion": "Nowa wersja LlamaCpp {{version}}",
|
||||
"backendUpdateAvailable": "Dostępna aktualizacja LlamaCpp",
|
||||
"remindMeLater": "Przypomnij mi później",
|
||||
"updating": "Aktualizowanie...",
|
||||
"updateNow": "Aktualizuj teraz",
|
||||
"updateSuccess": "LlamaCpp został pomyślnie zaktualizowany",
|
||||
"updateError": "Nie udało się zaktualizować LlamaCpp"
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,11 @@
|
||||
"noUpdateAvailable": "Bạn đang chạy phiên bản mới nhất",
|
||||
"devVersion": "Đã phát hiện phiên bản phát triển",
|
||||
"updateError": "Không thể kiểm tra cập nhật",
|
||||
"checkForBackendUpdates": "Kiểm tra Cập nhật LlamaCpp",
|
||||
"checkForBackendUpdatesDesc": "Kiểm tra xem có phiên bản backend LlamaCpp mới hơn không.",
|
||||
"checkingForBackendUpdates": "Đang kiểm tra cập nhật LlamaCpp...",
|
||||
"noBackendUpdateAvailable": "Bạn đang chạy phiên bản LlamaCpp mới nhất",
|
||||
"backendUpdateError": "Không thể kiểm tra cập nhật LlamaCpp",
|
||||
"changeLocation": "Thay đổi Vị trí",
|
||||
"copied": "Đã sao chép",
|
||||
"copyPath": "Sao chép Đường dẫn",
|
||||
@ -244,5 +249,14 @@
|
||||
"cancel": "Hủy",
|
||||
"changeLocation": "Thay đổi vị trí"
|
||||
}
|
||||
},
|
||||
"backendUpdater": {
|
||||
"newBackendVersion": "Phiên bản LlamaCpp mới {{version}}",
|
||||
"backendUpdateAvailable": "Có cập nhật LlamaCpp",
|
||||
"remindMeLater": "Nhắc tôi sau",
|
||||
"updating": "Đang cập nhật...",
|
||||
"updateNow": "Cập nhật ngay",
|
||||
"updateSuccess": "Cập nhật LlamaCpp thành công",
|
||||
"updateError": "Không thể cập nhật LlamaCpp"
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,11 @@
|
||||
"noUpdateAvailable": "您正在运行最新版本",
|
||||
"devVersion": "检测到开发版本",
|
||||
"updateError": "检查更新失败",
|
||||
"checkForBackendUpdates": "检查 LlamaCpp 更新",
|
||||
"checkForBackendUpdatesDesc": "检查是否有更新的 LlamaCpp 后端版本。",
|
||||
"checkingForBackendUpdates": "正在检查 LlamaCpp 更新...",
|
||||
"noBackendUpdateAvailable": "您正在运行最新的 LlamaCpp 版本",
|
||||
"backendUpdateError": "检查 LlamaCpp 更新失败",
|
||||
"changeLocation": "更改位置",
|
||||
"copied": "已复制",
|
||||
"copyPath": "复制路径",
|
||||
@ -244,5 +249,14 @@
|
||||
"cancel": "取消",
|
||||
"changeLocation": "更改位置"
|
||||
}
|
||||
},
|
||||
"backendUpdater": {
|
||||
"newBackendVersion": "新的 LlamaCpp 版本 {{version}}",
|
||||
"backendUpdateAvailable": "LlamaCpp 更新可用",
|
||||
"remindMeLater": "稍后提醒我",
|
||||
"updating": "正在更新...",
|
||||
"updateNow": "立即更新",
|
||||
"updateSuccess": "LlamaCpp 更新成功",
|
||||
"updateError": "更新 LlamaCpp 失败"
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,11 @@
|
||||
"noUpdateAvailable": "您正在運行最新版本",
|
||||
"devVersion": "檢測到開發版本",
|
||||
"updateError": "檢查更新失敗",
|
||||
"checkForBackendUpdates": "檢查 LlamaCpp 更新",
|
||||
"checkForBackendUpdatesDesc": "檢查是否有更新的 LlamaCpp 後端版本。",
|
||||
"checkingForBackendUpdates": "正在檢查 LlamaCpp 更新...",
|
||||
"noBackendUpdateAvailable": "您正在運行最新的 LlamaCpp 版本",
|
||||
"backendUpdateError": "檢查 LlamaCpp 更新失敗",
|
||||
"changeLocation": "更改位置",
|
||||
"copied": "已複製",
|
||||
"copyPath": "複製路徑",
|
||||
@ -244,5 +249,14 @@
|
||||
"cancel": "取消",
|
||||
"changeLocation": "變更位置"
|
||||
}
|
||||
},
|
||||
"backendUpdater": {
|
||||
"newBackendVersion": "新的 LlamaCpp 版本 {{version}}",
|
||||
"backendUpdateAvailable": "LlamaCpp 更新可用",
|
||||
"remindMeLater": "稍後提醒我",
|
||||
"updating": "正在更新...",
|
||||
"updateNow": "立即更新",
|
||||
"updateSuccess": "LlamaCpp 更新成功",
|
||||
"updateError": "更新 LlamaCpp 失敗"
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ import { createRootRoute, Outlet, useRouterState } from '@tanstack/react-router'
|
||||
|
||||
import LeftPanel from '@/containers/LeftPanel'
|
||||
import DialogAppUpdater from '@/containers/dialogs/AppUpdater'
|
||||
import BackendUpdater from '@/containers/dialogs/BackendUpdater'
|
||||
import { Fragment } from 'react/jsx-runtime'
|
||||
import { AppearanceProvider } from '@/providers/AppearanceProvider'
|
||||
import { ThemeProvider } from '@/providers/ThemeProvider'
|
||||
@ -113,6 +114,7 @@ const AppLayout = () => {
|
||||
{/* Fake absolute panel top to enable window drag */}
|
||||
<div className="absolute w-full h-10 z-10" data-tauri-drag-region />
|
||||
<DialogAppUpdater />
|
||||
<BackendUpdater />
|
||||
|
||||
{/* Use ResizablePanelGroup only on larger screens */}
|
||||
{!isSmallScreen && isLeftPanelOpen ? (
|
||||
@ -164,7 +166,9 @@ const AppLayout = () => {
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
{PlatformFeatures[PlatformFeature.ANALYTICS] && productAnalyticPrompt && <PromptAnalytic />}
|
||||
{PlatformFeatures[PlatformFeature.ANALYTICS] && productAnalyticPrompt && (
|
||||
<PromptAnalytic />
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
@ -27,7 +27,12 @@ import DeleteProvider from '@/containers/dialogs/DeleteProvider'
|
||||
import { useServiceHub } from '@/hooks/useServiceHub'
|
||||
import { localStorageKey } from '@/constants/localStorage'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { IconFolderPlus, IconLoader, IconRefresh } from '@tabler/icons-react'
|
||||
import {
|
||||
IconFolderPlus,
|
||||
IconLoader,
|
||||
IconRefresh,
|
||||
IconUpload,
|
||||
} from '@tabler/icons-react'
|
||||
import { toast } from 'sonner'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { predefinedProviders } from '@/consts/providers'
|
||||
@ -35,6 +40,7 @@ import { useModelLoad } from '@/hooks/useModelLoad'
|
||||
import { useLlamacppDevices } from '@/hooks/useLlamacppDevices'
|
||||
import { PlatformFeatures } from '@/lib/platform/const'
|
||||
import { PlatformFeature } from '@/lib/platform/types'
|
||||
import { useBackendUpdater } from '@/hooks/useBackendUpdater'
|
||||
|
||||
// as route.threadsDetail
|
||||
export const Route = createFileRoute('/settings/providers/$providerName')({
|
||||
@ -75,6 +81,10 @@ function ProviderDetail() {
|
||||
const [activeModels, setActiveModels] = useState<string[]>([])
|
||||
const [loadingModels, setLoadingModels] = useState<string[]>([])
|
||||
const [refreshingModels, setRefreshingModels] = useState(false)
|
||||
const [isCheckingBackendUpdate, setIsCheckingBackendUpdate] = useState(false)
|
||||
const [isInstallingBackend, setIsInstallingBackend] = useState(false)
|
||||
const { checkForUpdate: checkForBackendUpdate, installBackend } =
|
||||
useBackendUpdater()
|
||||
const { providerName } = useParams({ from: Route.id })
|
||||
const { getProviderByName, setProviders, updateProvider } = useModelProvider()
|
||||
const provider = getProviderByName(providerName)
|
||||
@ -310,6 +320,68 @@ function ProviderDetail() {
|
||||
})
|
||||
}
|
||||
|
||||
const handleCheckForBackendUpdate = useCallback(async () => {
|
||||
if (provider?.provider !== 'llamacpp') return
|
||||
|
||||
setIsCheckingBackendUpdate(true)
|
||||
try {
|
||||
const update = await checkForBackendUpdate(true)
|
||||
if (!update) {
|
||||
toast.info(t('settings:noBackendUpdateAvailable'))
|
||||
}
|
||||
// If update is available, the BackendUpdater dialog will automatically show
|
||||
} catch (error) {
|
||||
console.error('Failed to check for backend updates:', error)
|
||||
toast.error(t('settings:backendUpdateError'))
|
||||
} finally {
|
||||
setIsCheckingBackendUpdate(false)
|
||||
}
|
||||
}, [provider, checkForBackendUpdate, t])
|
||||
|
||||
const handleInstallBackendFromFile = useCallback(async () => {
|
||||
if (provider?.provider !== 'llamacpp') return
|
||||
|
||||
setIsInstallingBackend(true)
|
||||
try {
|
||||
// Open file dialog with filter for .tar.gz files
|
||||
const selectedFile = await serviceHub.dialog().open({
|
||||
multiple: false,
|
||||
directory: false,
|
||||
filters: [
|
||||
{
|
||||
name: 'Backend Archives',
|
||||
extensions: ['tar.gz'],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
if (selectedFile && typeof selectedFile === 'string') {
|
||||
// Process the file path: replace spaces with dashes and convert to lowercase
|
||||
const processedFilePath = selectedFile
|
||||
.replace(/\s+/g, '-')
|
||||
.toLowerCase()
|
||||
|
||||
// Install the backend using the llamacpp extension
|
||||
await installBackend(processedFilePath)
|
||||
|
||||
toast.success(t('settings:backendInstallSuccess'), {
|
||||
description: 'Backend installed successfully',
|
||||
})
|
||||
|
||||
// Refresh settings to update backend configuration
|
||||
await refreshSettings()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to install backend from file:', error)
|
||||
toast.error(t('settings:backendInstallError'), {
|
||||
description:
|
||||
error instanceof Error ? error.message : 'Unknown error occurred',
|
||||
})
|
||||
} finally {
|
||||
setIsInstallingBackend(false)
|
||||
}
|
||||
}, [provider, serviceHub, refreshSettings, t, installBackend])
|
||||
|
||||
// Check if model provider settings are enabled for this platform
|
||||
if (!PlatformFeatures[PlatformFeature.MODEL_PROVIDER_SETTINGS]) {
|
||||
return (
|
||||
@ -525,6 +597,60 @@ function ProviderDetail() {
|
||||
<span> is the recommended backend.</span>
|
||||
</div>
|
||||
)}
|
||||
{setting.key === 'version_backend' &&
|
||||
provider?.provider === 'llamacpp' && (
|
||||
<div className="mt-2 flex flex-wrap gap-2">
|
||||
<Button
|
||||
variant="link"
|
||||
size="sm"
|
||||
className="p-0"
|
||||
onClick={handleCheckForBackendUpdate}
|
||||
disabled={isCheckingBackendUpdate}
|
||||
>
|
||||
<div className="cursor-pointer flex items-center justify-center rounded-sm hover:bg-main-view-fg/15 bg-main-view-fg/10 transition-all duration-200 ease-in-out px-2 py-1 gap-1">
|
||||
<IconRefresh
|
||||
size={12}
|
||||
className={cn(
|
||||
'text-main-view-fg/50',
|
||||
isCheckingBackendUpdate &&
|
||||
'animate-spin'
|
||||
)}
|
||||
/>
|
||||
<span>
|
||||
{isCheckingBackendUpdate
|
||||
? t(
|
||||
'settings:checkingForBackendUpdates'
|
||||
)
|
||||
: t(
|
||||
'settings:checkForBackendUpdates'
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
size="sm"
|
||||
className="p-0"
|
||||
onClick={handleInstallBackendFromFile}
|
||||
disabled={isInstallingBackend}
|
||||
>
|
||||
<div className="cursor-pointer flex items-center justify-center rounded-sm hover:bg-main-view-fg/15 bg-main-view-fg/10 transition-all duration-200 ease-in-out px-2 py-1 gap-1">
|
||||
<IconUpload
|
||||
size={12}
|
||||
className={cn(
|
||||
'text-main-view-fg/50',
|
||||
isInstallingBackend && 'animate-pulse'
|
||||
)}
|
||||
/>
|
||||
<span>
|
||||
{isInstallingBackend
|
||||
? 'Installing Backend...'
|
||||
: 'Install Backend from File'}
|
||||
</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
actions={actionComponent}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user