jan/web-app/src/containers/ModelSetting.tsx
Akarshan Biswas a1af70f7a9
feat: Enhance Llama.cpp backend management with persistence (#5886)
* 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>
2025-07-24 18:33:35 +07:00

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>
)
}