fix: reverse setting local provider (#5140)

* fix: reverse setting local provider

* fix conflict
This commit is contained in:
Faisal Amir 2025-05-30 00:00:07 +07:00 committed by GitHub
parent e9c9205544
commit 27c2a360f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -240,264 +240,293 @@ function ProviderDetail() {
/> />
</div> </div>
{/* Settings */} <div
<Card> className={cn(
{provider?.settings.map((setting, settingIndex) => { 'flex flex-col gap-3',
// Use the DynamicController component provider &&
const actionComponent = ( provider.provider === 'llama.cpp' &&
<div className="mt-2"> 'flex-col-reverse'
<DynamicControllerSetting )}
controllerType={setting.controller_type} >
controllerProps={setting.controller_props} {/* Settings */}
className={cn( <Card>
setting.key === 'api-key' && {provider?.settings.map((setting, settingIndex) => {
'third-step-setup-remote-provider' // Use the DynamicController component
)} const actionComponent = (
onChange={(newValue) => { <div className="mt-2">
if (provider) { <DynamicControllerSetting
const newSettings = [...provider.settings] controllerType={setting.controller_type}
// Handle different value types by forcing the type controllerProps={setting.controller_props}
// Use type assertion to bypass type checking className={cn(
setting.key === 'api-key' &&
'third-step-setup-remote-provider'
)}
onChange={(newValue) => {
if (provider) {
const newSettings = [...provider.settings]
// Handle different value types by forcing the type
// Use type assertion to bypass type checking
;( ;(
newSettings[settingIndex].controller_props as { newSettings[settingIndex].controller_props as {
value: string | boolean | number value: string | boolean | number
}
).value = newValue
// Create update object with updated settings
const updateObj: Partial<ModelProvider> = {
settings: newSettings,
} }
).value = newValue // Check if this is an API key or base URL setting and update the corresponding top-level field
const settingKey = setting.key
// Create update object with updated settings if (
const updateObj: Partial<ModelProvider> = { settingKey === 'api-key' &&
settings: newSettings, typeof newValue === 'string'
} ) {
// Check if this is an API key or base URL setting and update the corresponding top-level field updateObj.api_key = newValue
const settingKey = setting.key } else if (
if ( settingKey === 'base-url' &&
settingKey === 'api-key' && typeof newValue === 'string'
typeof newValue === 'string' ) {
) { updateObj.base_url = newValue
updateObj.api_key = newValue }
} else if ( updateSettings(
settingKey === 'base-url' && providerName,
typeof newValue === 'string' updateObj.settings ?? []
) {
updateObj.base_url = newValue
}
updateSettings(
providerName,
updateObj.settings ?? []
)
updateProvider(providerName, {
...provider,
...updateObj,
})
}
}}
/>
</div>
)
return (
<CardItem
key={settingIndex}
title={setting.title}
column={
setting.controller_type === 'input' &&
setting.controller_props.type !== 'number'
? true
: false
}
description={
<RenderMarkdown
className="![>p]:text-main-view-fg/70 select-none"
content={setting.description}
components={{
// Make links open in a new tab
a: ({ ...props }) => {
return (
<a
{...props}
target="_blank"
rel="noopener noreferrer"
className={cn(
setting.key === 'api-key' &&
'second-step-setup-remote-provider'
)}
/>
) )
}, updateProvider(providerName, {
p: ({ ...props }) => ( ...provider,
<p {...props} className="!mb-0" /> ...updateObj,
), })
}
}} }}
/> />
} </div>
actions={actionComponent} )
/>
)
})}
<DeleteProvider provider={provider} /> return (
</Card> <CardItem
key={settingIndex}
title={setting.title}
column={
setting.controller_type === 'input' &&
setting.controller_props.type !== 'number'
? true
: false
}
description={
<RenderMarkdown
className="![>p]:text-main-view-fg/70 select-none"
content={setting.description}
components={{
// Make links open in a new tab
a: ({ ...props }) => {
return (
<a
{...props}
target="_blank"
rel="noopener noreferrer"
className={cn(
setting.key === 'api-key' &&
'second-step-setup-remote-provider'
)}
/>
)
},
p: ({ ...props }) => (
<p {...props} className="!mb-0" />
),
}}
/>
}
actions={actionComponent}
/>
)
})}
{/* Models */} <DeleteProvider provider={provider} />
<Card </Card>
header={
<div className="flex items-center justify-between mb-4"> {/* Models */}
<h1 className="text-main-view-fg font-medium text-base"> <Card
Models header={
</h1> <div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2"> <h1 className="text-main-view-fg font-medium text-base">
{provider && provider.provider !== 'llama.cpp' && ( Models
<> </h1>
<div className="flex items-center gap-2">
{provider && provider.provider !== 'llama.cpp' && (
<>
<Button
variant="link"
size="sm"
className="hover:no-underline"
onClick={handleRefreshModels}
disabled={refreshingModels}
>
<div className="cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/15 bg-main-view-fg/10 transition-all duration-200 ease-in-out px-1.5 py-1 gap-1">
{refreshingModels ? (
<IconLoader
size={18}
className="text-main-view-fg/50 animate-spin"
/>
) : (
<IconRefresh
size={18}
className="text-main-view-fg/50"
/>
)}
<span className="text-main-view-fg/70">
{refreshingModels
? 'Refreshing...'
: 'Refresh'}
</span>
</div>
</Button>
<DialogAddModel provider={provider} />
</>
)}
{provider && provider.provider === 'llama.cpp' && (
<Button <Button
variant="link" variant="link"
size="sm" size="sm"
className="hover:no-underline" className="hover:no-underline"
onClick={handleRefreshModels} onClick={async () => {
disabled={refreshingModels} const selectedFile = await open({
multiple: false,
directory: false,
filters: [
{
name: 'GGUF',
extensions: ['gguf'],
},
],
})
if (selectedFile) {
try {
await importModel(selectedFile)
} catch (error) {
console.error(
'Failed to import model:',
error
)
} finally {
// Refresh the provider to update the models list
getProviders().then(setProviders)
toast.success('Import Model', {
id: `import-model-${provider.provider}`,
description: `Model ${provider.provider} has been imported successfully.`,
})
}
}
}}
> >
<div className="cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/15 bg-main-view-fg/10 transition-all duration-200 ease-in-out px-1.5 py-1 gap-1"> <div className="cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/15 bg-main-view-fg/10 transition-all duration-200 ease-in-out p-1.5 py-1 gap-1 -mr-2">
{refreshingModels ? ( <IconFolderPlus
<IconLoader size={18}
size={18} className="text-main-view-fg/50"
className="text-main-view-fg/50 animate-spin" />
/>
) : (
<IconRefresh
size={18}
className="text-main-view-fg/50"
/>
)}
<span className="text-main-view-fg/70"> <span className="text-main-view-fg/70">
{refreshingModels ? 'Refreshing...' : 'Refresh'} Import
</span> </span>
</div> </div>
</Button> </Button>
<DialogAddModel provider={provider} /> )}
</> </div>
)}
{provider && provider.provider === 'llama.cpp' && (
<Button
variant="link"
size="sm"
className="hover:no-underline"
onClick={async () => {
const selectedFile = await open({
multiple: false,
directory: false,
filters: [
{
name: 'GGUF',
extensions: ['gguf'],
},
],
})
if (selectedFile) {
try {
await importModel(selectedFile)
} catch (error) {
console.error('Failed to import model:', error)
} finally {
// Refresh the provider to update the models list
getProviders().then(setProviders)
toast.success('Import Model', {
id: `import-model-${provider.provider}`,
description: `Model ${provider.provider} has been imported successfully.`,
})
}
}
}}
>
<div className="cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/15 bg-main-view-fg/10 transition-all duration-200 ease-in-out px-1.5 py-1 gap-1">
<IconFolderPlus
size={18}
className="text-main-view-fg/50"
/>
<span className="text-main-view-fg/70">Import</span>
</div>
</Button>
)}
</div> </div>
</div> }
} >
> {provider?.models.length ? (
{provider?.models.length ? ( provider?.models.map((model, modelIndex) => {
provider?.models.map((model, modelIndex) => { const capabilities = model.capabilities || []
const capabilities = model.capabilities || [] return (
return ( <CardItem
<CardItem key={modelIndex}
key={modelIndex} title={
title={ <div className="flex items-center gap-2">
<div className="flex items-center gap-2"> <h1 className="font-medium">{model.id}</h1>
<h1 className="font-medium">{model.id}</h1> <Capabilities capabilities={capabilities} />
<Capabilities capabilities={capabilities} /> </div>
</div> }
} actions={
actions={ <div className="flex items-center gap-1">
<div className="flex items-center gap-1"> <DialogEditModel
{provider && provider.provider === 'llama.cpp' && ( provider={provider}
<div className="mr-1"> modelId={model.id}
{activeModels.some( />
(activeModel) => activeModel.id === model.id {model.settings && (
) ? ( <ModelSetting
<Button provider={provider}
size="sm" model={model}
variant="destructive" />
onClick={() => handleStopModel(model.id)} )}
> <DialogDeleteModel
Stop provider={provider}
</Button> modelId={model.id}
) : ( />
<Button {provider &&
size="sm" provider.provider === 'llama.cpp' && (
disabled={loadingModels.includes(model.id)} <div className="ml-2">
onClick={() => handleStartModel(model.id)} {activeModels.some(
> (activeModel) =>
{loadingModels.includes(model.id) ? ( activeModel.id === model.id
<div className="flex items-center gap-2"> ) ? (
<IconLoader <Button
size={16} size="sm"
className="animate-spin" variant="destructive"
/> onClick={() =>
</div> handleStopModel(model.id)
}
>
Stop
</Button>
) : ( ) : (
'Start' <Button
size="sm"
disabled={loadingModels.includes(
model.id
)}
onClick={() =>
handleStartModel(model.id)
}
>
{loadingModels.includes(model.id) ? (
<div className="flex items-center gap-2">
<IconLoader
size={16}
className="animate-spin"
/>
</div>
) : (
'Start'
)}
</Button>
)} )}
</Button> </div>
)} )}
</div> </div>
)} }
<DialogEditModel />
provider={provider} )
modelId={model.id} })
/> ) : (
{model.settings && ( <div className="-mt-2">
<ModelSetting provider={provider} model={model} /> <div className="flex items-center gap-2 text-left-panel-fg/80">
)} <h6 className="font-medium text-base">
<DialogDeleteModel No model found
provider={provider} </h6>
modelId={model.id} </div>
/> <p className="text-left-panel-fg/60 mt-1 text-xs leading-relaxed">
</div> Available models will be listed here. If you don't have
} any models yet, visit the&nbsp;
/> <Link to={route.hub}>Hub</Link>
) &nbsp;to download.
}) </p>
) : (
<div className="-mt-2">
<div className="flex items-center gap-2 text-left-panel-fg/80">
<h6 className="font-medium text-base">No model found</h6>
</div> </div>
<p className="text-left-panel-fg/60 mt-1 text-xs leading-relaxed"> )}
Available models will be listed here. If you don't have </Card>
any models yet, visit the&nbsp; </div>
<Link to={route.hub}>Hub</Link>
&nbsp;to download.
</p>
</div>
)}
</Card>
</div> </div>
</div> </div>
</div> </div>