chore: update model setting include offload-mmproj
This commit is contained in:
parent
00674ec0d5
commit
fdc8e07f86
@ -1548,6 +1548,26 @@ export default class llamacpp_extension extends AIEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if mmproj.gguf file exists for a given model ID
|
||||||
|
* @param modelId - The model ID to check for mmproj.gguf
|
||||||
|
* @returns Promise<boolean> - true if mmproj.gguf exists, false otherwise
|
||||||
|
*/
|
||||||
|
async checkMmprojExists(modelId: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const mmprojPath = await joinPath([
|
||||||
|
await this.getProviderPath(),
|
||||||
|
'models',
|
||||||
|
modelId,
|
||||||
|
'mmproj.gguf',
|
||||||
|
])
|
||||||
|
return await fs.existsSync(mmprojPath)
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(`Error checking mmproj.gguf for model ${modelId}:`, e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getDevices(): Promise<DeviceList[]> {
|
async getDevices(): Promise<DeviceList[]> {
|
||||||
const cfg = this.config
|
const cfg = this.config
|
||||||
const [version, backend] = cfg.version_backend.split('/')
|
const [version, backend] = cfg.version_backend.split('/')
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import { localStorageKey } from '@/constants/localStorage'
|
|||||||
import { useTranslation } from '@/i18n/react-i18next-compat'
|
import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||||
import { useFavoriteModel } from '@/hooks/useFavoriteModel'
|
import { useFavoriteModel } from '@/hooks/useFavoriteModel'
|
||||||
import { predefinedProviders } from '@/consts/providers'
|
import { predefinedProviders } from '@/consts/providers'
|
||||||
|
import { checkMmprojExists } from '@/services/models'
|
||||||
|
|
||||||
type DropdownModelProviderProps = {
|
type DropdownModelProviderProps = {
|
||||||
model?: ThreadModel
|
model?: ThreadModel
|
||||||
@ -66,6 +67,7 @@ const DropdownModelProvider = ({
|
|||||||
getModelBy,
|
getModelBy,
|
||||||
selectedProvider,
|
selectedProvider,
|
||||||
selectedModel,
|
selectedModel,
|
||||||
|
updateProvider,
|
||||||
} = useModelProvider()
|
} = useModelProvider()
|
||||||
const [displayModel, setDisplayModel] = useState<string>('')
|
const [displayModel, setDisplayModel] = useState<string>('')
|
||||||
const { updateCurrentThreadModel } = useThreads()
|
const { updateCurrentThreadModel } = useThreads()
|
||||||
@ -79,31 +81,52 @@ const DropdownModelProvider = ({
|
|||||||
const searchInputRef = useRef<HTMLInputElement>(null)
|
const searchInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
// Helper function to check if a model exists in providers
|
// Helper function to check if a model exists in providers
|
||||||
const checkModelExists = useCallback((providerName: string, modelId: string) => {
|
const checkModelExists = useCallback(
|
||||||
|
(providerName: string, modelId: string) => {
|
||||||
const provider = providers.find(
|
const provider = providers.find(
|
||||||
(p) => p.provider === providerName && p.active
|
(p) => p.provider === providerName && p.active
|
||||||
)
|
)
|
||||||
return provider?.models.find((m) => m.id === modelId)
|
return provider?.models.find((m) => m.id === modelId)
|
||||||
}, [providers])
|
},
|
||||||
|
[providers]
|
||||||
|
)
|
||||||
|
|
||||||
// Initialize model provider only once
|
// Initialize model provider only once
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const initializeModel = async () => {
|
||||||
// Auto select model when existing thread is passed
|
// Auto select model when existing thread is passed
|
||||||
if (model) {
|
if (model) {
|
||||||
selectModelProvider(model?.provider as string, model?.id as string)
|
selectModelProvider(model?.provider as string, model?.id as string)
|
||||||
if (!checkModelExists(model.provider, model.id)) {
|
if (!checkModelExists(model.provider, model.id)) {
|
||||||
selectModelProvider('', '')
|
selectModelProvider('', '')
|
||||||
}
|
}
|
||||||
|
// Check mmproj existence for llamacpp models
|
||||||
|
if (model?.provider === 'llamacpp') {
|
||||||
|
await checkMmprojExists(
|
||||||
|
model.id as string,
|
||||||
|
updateProvider,
|
||||||
|
getProviderByName
|
||||||
|
)
|
||||||
|
}
|
||||||
} else if (useLastUsedModel) {
|
} else if (useLastUsedModel) {
|
||||||
// Try to use last used model only when explicitly requested (for new chat)
|
// Try to use last used model only when explicitly requested (for new chat)
|
||||||
const lastUsed = getLastUsedModel()
|
const lastUsed = getLastUsedModel()
|
||||||
if (lastUsed && checkModelExists(lastUsed.provider, lastUsed.model)) {
|
if (lastUsed && checkModelExists(lastUsed.provider, lastUsed.model)) {
|
||||||
selectModelProvider(lastUsed.provider, lastUsed.model)
|
selectModelProvider(lastUsed.provider, lastUsed.model)
|
||||||
|
if (lastUsed.provider === 'llamacpp') {
|
||||||
|
await checkMmprojExists(
|
||||||
|
lastUsed.model,
|
||||||
|
updateProvider,
|
||||||
|
getProviderByName
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback to default model if last used model no longer exists
|
|
||||||
selectModelProvider('', '')
|
selectModelProvider('', '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeModel()
|
||||||
}, [
|
}, [
|
||||||
model,
|
model,
|
||||||
selectModelProvider,
|
selectModelProvider,
|
||||||
@ -111,6 +134,8 @@ const DropdownModelProvider = ({
|
|||||||
providers,
|
providers,
|
||||||
useLastUsedModel,
|
useLastUsedModel,
|
||||||
checkModelExists,
|
checkModelExists,
|
||||||
|
updateProvider,
|
||||||
|
getProviderByName,
|
||||||
])
|
])
|
||||||
|
|
||||||
// Update display model when selection changes
|
// Update display model when selection changes
|
||||||
@ -245,7 +270,7 @@ const DropdownModelProvider = ({
|
|||||||
}, [filteredItems, providers, searchValue, favoriteModels])
|
}, [filteredItems, providers, searchValue, favoriteModels])
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
(searchableModel: SearchableModel) => {
|
async (searchableModel: SearchableModel) => {
|
||||||
selectModelProvider(
|
selectModelProvider(
|
||||||
searchableModel.provider.provider,
|
searchableModel.provider.provider,
|
||||||
searchableModel.model.id
|
searchableModel.model.id
|
||||||
@ -254,6 +279,16 @@ const DropdownModelProvider = ({
|
|||||||
id: searchableModel.model.id,
|
id: searchableModel.model.id,
|
||||||
provider: searchableModel.provider.provider,
|
provider: searchableModel.provider.provider,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Check mmproj existence for llamacpp models
|
||||||
|
if (searchableModel.provider.provider === 'llamacpp') {
|
||||||
|
await checkMmprojExists(
|
||||||
|
searchableModel.model.id,
|
||||||
|
updateProvider,
|
||||||
|
getProviderByName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Store the selected model as last used
|
// Store the selected model as last used
|
||||||
if (useLastUsedModel) {
|
if (useLastUsedModel) {
|
||||||
setLastUsedModel(
|
setLastUsedModel(
|
||||||
@ -264,7 +299,13 @@ const DropdownModelProvider = ({
|
|||||||
setSearchValue('')
|
setSearchValue('')
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
},
|
},
|
||||||
[selectModelProvider, updateCurrentThreadModel, useLastUsedModel]
|
[
|
||||||
|
selectModelProvider,
|
||||||
|
updateCurrentThreadModel,
|
||||||
|
useLastUsedModel,
|
||||||
|
updateProvider,
|
||||||
|
getProviderByName,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
const currentModel = selectedModel?.id
|
const currentModel = selectedModel?.id
|
||||||
|
|||||||
@ -70,8 +70,8 @@ export function ModelSetting({
|
|||||||
models: updatedModels,
|
models: updatedModels,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Call debounced stopModel only when updating ctx_len or ngl
|
// Call debounced stopModel only when updating ctx_len, ngl, chat_template, or offload_mmproj
|
||||||
if (key === 'ctx_len' || key === 'ngl' || key === 'chat_template') {
|
if (key === 'ctx_len' || key === 'ngl' || key === 'chat_template' || key === 'offload_mmproj') {
|
||||||
debouncedStopModel(model.id)
|
debouncedStopModel(model.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { sanitizeModelId } from '@/lib/utils'
|
import { sanitizeModelId } from '@/lib/utils'
|
||||||
import {
|
import {
|
||||||
AIEngine,
|
AIEngine,
|
||||||
@ -350,3 +351,117 @@ export const isToolSupported = async (modelId: string): Promise<boolean> => {
|
|||||||
|
|
||||||
return engine.isToolSupported(modelId)
|
return engine.isToolSupported(modelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if mmproj.gguf file exists for a given model ID in the llamacpp provider.
|
||||||
|
* Also checks if the model has offload_mmproj setting.
|
||||||
|
* If mmproj.gguf exists, adds offload_mmproj setting with value true.
|
||||||
|
* @param modelId - The model ID to check for mmproj.gguf
|
||||||
|
* @param updateProvider - Function to update the provider state
|
||||||
|
* @param getProviderByName - Function to get provider by name
|
||||||
|
* @returns Promise<{exists: boolean, settingsUpdated: boolean}> - exists: true if mmproj.gguf exists, settingsUpdated: true if settings were modified
|
||||||
|
*/
|
||||||
|
export const checkMmprojExists = async (
|
||||||
|
modelId: string,
|
||||||
|
updateProvider?: (providerName: string, data: Partial<ModelProvider>) => void,
|
||||||
|
getProviderByName?: (providerName: string) => ModelProvider | undefined
|
||||||
|
): Promise<{ exists: boolean; settingsUpdated: boolean }> => {
|
||||||
|
let settingsUpdated = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
const engine = getEngine('llamacpp') as AIEngine & {
|
||||||
|
checkMmprojExists?: (id: string) => Promise<boolean>
|
||||||
|
}
|
||||||
|
if (engine && typeof engine.checkMmprojExists === 'function') {
|
||||||
|
const exists = await engine.checkMmprojExists(modelId)
|
||||||
|
|
||||||
|
// If we have the store functions, use them; otherwise fall back to localStorage
|
||||||
|
if (updateProvider && getProviderByName) {
|
||||||
|
const provider = getProviderByName('llamacpp')
|
||||||
|
if (provider) {
|
||||||
|
const model = provider.models.find((m) => m.id === modelId)
|
||||||
|
|
||||||
|
if (model?.settings) {
|
||||||
|
const hasOffloadMmproj = 'offload_mmproj' in model.settings
|
||||||
|
|
||||||
|
// If mmproj exists, add offload_mmproj setting (only if it doesn't exist)
|
||||||
|
if (exists && !hasOffloadMmproj) {
|
||||||
|
// Create updated models array with the new setting
|
||||||
|
const updatedModels = provider.models.map((m) => {
|
||||||
|
if (m.id === modelId) {
|
||||||
|
return {
|
||||||
|
...m,
|
||||||
|
settings: {
|
||||||
|
...m.settings,
|
||||||
|
offload_mmproj: {
|
||||||
|
key: 'offload_mmproj',
|
||||||
|
title: 'Offload MMProj',
|
||||||
|
description:
|
||||||
|
'Offload multimodal projection layers to GPU',
|
||||||
|
controller_type: 'checkbox',
|
||||||
|
controller_props: {
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update the provider with the new models array
|
||||||
|
updateProvider('llamacpp', { models: updatedModels })
|
||||||
|
settingsUpdated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fall back to localStorage approach for backwards compatibility
|
||||||
|
try {
|
||||||
|
const modelProviderData = JSON.parse(
|
||||||
|
localStorage.getItem('model-provider') || '{}'
|
||||||
|
)
|
||||||
|
const llamacppProvider = modelProviderData.state?.providers?.find(
|
||||||
|
(p: any) => p.provider === 'llamacpp'
|
||||||
|
)
|
||||||
|
const model = llamacppProvider?.models?.find(
|
||||||
|
(m: any) => m.id === modelId
|
||||||
|
)
|
||||||
|
|
||||||
|
if (model?.settings) {
|
||||||
|
// If mmproj exists, add offload_mmproj setting (only if it doesn't exist)
|
||||||
|
if (exists) {
|
||||||
|
if (!model.settings.offload_mmproj) {
|
||||||
|
model.settings.offload_mmproj = {
|
||||||
|
key: 'offload_mmproj',
|
||||||
|
title: 'Offload MMProj',
|
||||||
|
description: 'Offload multimodal projection layers to GPU',
|
||||||
|
controller_type: 'checkbox',
|
||||||
|
controller_props: {
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Save updated settings back to localStorage
|
||||||
|
localStorage.setItem(
|
||||||
|
'model-provider',
|
||||||
|
JSON.stringify(modelProviderData)
|
||||||
|
)
|
||||||
|
settingsUpdated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (localStorageError) {
|
||||||
|
console.error(
|
||||||
|
`Error checking localStorage for model ${modelId}:`,
|
||||||
|
localStorageError
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { exists, settingsUpdated }
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error checking mmproj for model ${modelId}:`, error)
|
||||||
|
}
|
||||||
|
return { exists: false, settingsUpdated }
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user