* feat: Enhance Llama.cpp backend management with persistence This commit introduces significant improvements to how the Llama.cpp extension manages and updates its backend installations, focusing on user preference persistence and smarter auto-updates. Key changes include: * **Persistent Backend Type Preference:** The extension now stores the user's preferred backend type (e.g., `cuda`, `cpu`, `metal`) in `localStorage`. This ensures that even after updates or restarts, the system attempts to use the user's previously selected backend type, if available. * **Intelligent Auto-Update:** The auto-update mechanism has been refined to prioritize updating to the **latest version of the *currently selected backend type*** rather than always defaulting to the "best available" backend (which might change). This respects user choice while keeping the chosen backend type up-to-date. * **Improved Initial Installation/Configuration:** For fresh installations or cases where the `version_backend` setting is invalid, the system now intelligently determines and installs the best available backend, then persists its type. * **Refined Old Backend Cleanup:** The `removeOldBackends` function has been renamed to `removeOldBackend` and modified to specifically clean up *older versions of the currently selected backend type*, preventing the accumulation of unnecessary files while preserving other backend types the user might switch to. * **Robust Local Storage Handling:** New private methods (`getStoredBackendType`, `setStoredBackendType`, `clearStoredBackendType`) are introduced to safely interact with `localStorage`, including error handling for potential `localStorage` access issues. * **Version Filtering Utility:** A new utility `findLatestVersionForBackend` helps in identifying the latest available version for a specific backend type from a list of supported backends. These changes provide a more stable, user-friendly, and maintainable backend management experience for the Llama.cpp extension. Fixes: #5883 * fix: cortex models migration should be done once * feat: Optimize Llama.cpp backend preference storage and UI updates This commit refines the Llama.cpp extension's backend management by: * **Optimizing `localStorage` Writes:** The system now only writes the backend type preference to `localStorage` if the new value is different from the currently stored one. This reduces unnecessary `localStorage` operations. * **Ensuring UI Consistency on Initial Setup:** When a fresh installation or an invalid backend configuration is detected, the UI settings are now explicitly updated to reflect the newly determined `effectiveBackendString`, ensuring the displayed setting matches the active configuration. These changes improve performance by reducing redundant storage operations and enhance user experience by maintaining UI synchronization with the backend state. * Revert "fix: provider settings should be refreshed on page load (#5887)" This reverts commit ce6af62c7df4a7e7ea8c0896f307309d6bf38771. * fix: add loader version backend llamacpp * fix: wrong key name * fix: model setting issues * fix: virtual dom hub * chore: cleanup * chore: hide device ofload setting --------- Co-authored-by: Louis <louis@jan.ai> Co-authored-by: Faisal Amir <urmauur@gmail.com>
133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
import { IconSettings } from '@tabler/icons-react'
|
|
import debounce from 'lodash.debounce'
|
|
|
|
import {
|
|
Sheet,
|
|
SheetContent,
|
|
SheetDescription,
|
|
SheetHeader,
|
|
SheetTitle,
|
|
SheetTrigger,
|
|
} from '@/components/ui/sheet'
|
|
import { DynamicControllerSetting } from '@/containers/dynamicControllerSetting'
|
|
import { useModelProvider } from '@/hooks/useModelProvider'
|
|
import { stopModel } from '@/services/models'
|
|
import { cn } from '@/lib/utils'
|
|
import { useTranslation } from '@/i18n/react-i18next-compat'
|
|
|
|
type ModelSettingProps = {
|
|
provider: ProviderObject
|
|
model: Model
|
|
smallIcon?: boolean
|
|
}
|
|
|
|
export function ModelSetting({
|
|
model,
|
|
provider,
|
|
smallIcon,
|
|
}: ModelSettingProps) {
|
|
const { updateProvider } = useModelProvider()
|
|
const { t } = useTranslation()
|
|
|
|
// Create a debounced version of stopModel that waits 500ms after the last call
|
|
const debouncedStopModel = debounce((modelId: string) => {
|
|
stopModel(modelId)
|
|
}, 500)
|
|
|
|
const handleSettingChange = (
|
|
key: string,
|
|
value: string | boolean | number
|
|
) => {
|
|
if (!provider) return
|
|
|
|
// Create a copy of the model with updated settings
|
|
const updatedModel = {
|
|
...model,
|
|
settings: {
|
|
...model.settings,
|
|
[key]: {
|
|
...(model.settings?.[key] != null ? model.settings?.[key] : {}),
|
|
controller_props: {
|
|
...(model.settings?.[key]?.controller_props ?? {}),
|
|
value: value,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Find the model index in the provider's models array
|
|
const modelIndex = provider.models.findIndex((m) => m.id === model.id)
|
|
|
|
if (modelIndex !== -1) {
|
|
// Create a copy of the provider's models array
|
|
const updatedModels = [...provider.models]
|
|
|
|
// Update the specific model in the array
|
|
updatedModels[modelIndex] = updatedModel as Model
|
|
|
|
// Update the provider with the new models array
|
|
updateProvider(provider.provider, {
|
|
models: updatedModels,
|
|
})
|
|
|
|
// Call debounced stopModel only when updating ctx_len or ngl
|
|
if (key === 'ctx_len' || key === 'ngl') {
|
|
debouncedStopModel(model.id)
|
|
}
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Sheet>
|
|
<SheetTrigger asChild>
|
|
<div
|
|
className={cn(
|
|
'size-6 cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out',
|
|
smallIcon && 'size-5'
|
|
)}
|
|
>
|
|
<IconSettings size={18} className="text-main-view-fg/50" />
|
|
</div>
|
|
</SheetTrigger>
|
|
<SheetContent className="h-[calc(100%-8px)] top-1 right-1 rounded-e-md overflow-y-auto">
|
|
<SheetHeader>
|
|
<SheetTitle>
|
|
{t('common:modelSettings.title', { modelId: model.id })}
|
|
</SheetTitle>
|
|
<SheetDescription>
|
|
{t('common:modelSettings.description')}
|
|
</SheetDescription>
|
|
</SheetHeader>
|
|
<div className="px-4 space-y-6">
|
|
{Object.entries(model.settings || {}).map(([key, value]) => {
|
|
const config = value as ProviderSetting
|
|
return (
|
|
<div key={key} className="space-y-2">
|
|
<div className="flex items-start justify-between gap-8">
|
|
<div className="space-y-1 mb-2">
|
|
<h3 className="font-medium">{config.title}</h3>
|
|
<p className="text-main-view-fg/70 text-xs">
|
|
{config.description}
|
|
</p>
|
|
</div>
|
|
<DynamicControllerSetting
|
|
key={config.key}
|
|
title={config.title}
|
|
description={config.description}
|
|
controllerType={config.controller_type}
|
|
controllerProps={{
|
|
...config.controller_props,
|
|
value: config.controller_props?.value,
|
|
}}
|
|
onChange={(newValue) => handleSettingChange(key, newValue)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
</SheetContent>
|
|
</Sheet>
|
|
)
|
|
}
|