Merge pull request #6233 from menloresearch/fix/import-model
fix: improve ux import model
This commit is contained in:
commit
6203a93325
55
src-tauri/Cargo.lock
generated
55
src-tauri/Cargo.lock
generated
@ -854,8 +854,18 @@ version = "0.20.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.20.11",
|
||||||
"darling_macro",
|
"darling_macro 0.20.11",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08440b3dd222c3d0433e63e097463969485f112baff337dfdaca043a0d760570"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core 0.21.2",
|
||||||
|
"darling_macro 0.21.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -872,13 +882,38 @@ dependencies = [
|
|||||||
"syn 2.0.104",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d25b7912bc28a04ab1b7715a68ea03aaa15662b43a1a4b2c480531fd19f8bf7e"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn 2.0.104",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.20.11"
|
version = "0.20.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.20.11",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.104",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce154b9bea7fb0c8e8326e62d00354000c36e79770ff21b8c84e3aa267d9d531"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core 0.21.2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn 2.0.104",
|
||||||
]
|
]
|
||||||
@ -3984,8 +4019,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rmcp"
|
name = "rmcp"
|
||||||
version = "0.2.1"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=3196c95f1dfafbffbdcdd6d365c94969ac975e6a#3196c95f1dfafbffbdcdd6d365c94969ac975e6a"
|
source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=209dbac50f51737ad953c3a2c8e28f3619b6c277#209dbac50f51737ad953c3a2c8e28f3619b6c277"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -4010,10 +4045,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rmcp-macros"
|
name = "rmcp-macros"
|
||||||
version = "0.2.1"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=3196c95f1dfafbffbdcdd6d365c94969ac975e6a#3196c95f1dfafbffbdcdd6d365c94969ac975e6a"
|
source = "git+https://github.com/modelcontextprotocol/rust-sdk?rev=209dbac50f51737ad953c3a2c8e28f3619b6c277#209dbac50f51737ad953c3a2c8e28f3619b6c277"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.21.2",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -4408,7 +4443,7 @@ version = "3.14.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f"
|
checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.20.11",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn 2.0.104",
|
||||||
@ -6868,7 +6903,7 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e"
|
checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.20.11",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.104",
|
"syn 2.0.104",
|
||||||
|
|||||||
@ -79,6 +79,7 @@ function ProviderDetail() {
|
|||||||
const [activeModels, setActiveModels] = useState<string[]>([])
|
const [activeModels, setActiveModels] = useState<string[]>([])
|
||||||
const [loadingModels, setLoadingModels] = useState<string[]>([])
|
const [loadingModels, setLoadingModels] = useState<string[]>([])
|
||||||
const [refreshingModels, setRefreshingModels] = useState(false)
|
const [refreshingModels, setRefreshingModels] = useState(false)
|
||||||
|
const [importingModel, setImportingModel] = useState(false)
|
||||||
const { providerName } = useParams({ from: Route.id })
|
const { providerName } = useParams({ from: Route.id })
|
||||||
const { getProviderByName, setProviders, updateProvider } = useModelProvider()
|
const { getProviderByName, setProviders, updateProvider } = useModelProvider()
|
||||||
const provider = getProviderByName(providerName)
|
const provider = getProviderByName(providerName)
|
||||||
@ -95,6 +96,72 @@ function ProviderDetail() {
|
|||||||
!setting.controller_props.value)
|
!setting.controller_props.value)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleImportModel = async () => {
|
||||||
|
if (!provider) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setImportingModel(true)
|
||||||
|
const selectedFile = await open({
|
||||||
|
multiple: false,
|
||||||
|
directory: false,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: 'GGUF',
|
||||||
|
extensions: ['gguf'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
// If the dialog returns a file path, extract just the file name
|
||||||
|
const fileName =
|
||||||
|
typeof selectedFile === 'string'
|
||||||
|
? selectedFile
|
||||||
|
.split(/[\\/]/)
|
||||||
|
.pop()
|
||||||
|
?.replace(/\s/g, '-')
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
if (selectedFile && fileName) {
|
||||||
|
// Check if model already exists
|
||||||
|
const modelExists = provider.models.some(
|
||||||
|
(model) => model.name === fileName
|
||||||
|
)
|
||||||
|
|
||||||
|
if (modelExists) {
|
||||||
|
toast.error('Model already exists', {
|
||||||
|
description: `${fileName} already imported`,
|
||||||
|
})
|
||||||
|
setImportingModel(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pullModel(fileName, selectedFile)
|
||||||
|
// Refresh the provider to update the models list
|
||||||
|
await getProviders().then(setProviders)
|
||||||
|
toast.success(t('providers:import'), {
|
||||||
|
id: `import-model-${provider.provider}`,
|
||||||
|
description: t(
|
||||||
|
'providers:importModelSuccess',
|
||||||
|
{ provider: fileName }
|
||||||
|
),
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
t('providers:importModelError'),
|
||||||
|
error
|
||||||
|
)
|
||||||
|
toast.error(t('providers:importModelError'), {
|
||||||
|
description: error instanceof Error ? error.message : 'Unknown error occurred',
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setImportingModel(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setImportingModel(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Initial data fetch
|
// Initial data fetch
|
||||||
getActiveModels().then((models) => setActiveModels(models || []))
|
getActiveModels().then((models) => setActiveModels(models || []))
|
||||||
@ -482,52 +549,23 @@ function ProviderDetail() {
|
|||||||
variant="link"
|
variant="link"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="hover:no-underline"
|
className="hover:no-underline"
|
||||||
onClick={async () => {
|
disabled={importingModel}
|
||||||
const selectedFile = await open({
|
onClick={handleImportModel}
|
||||||
multiple: false,
|
|
||||||
directory: false,
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
name: 'GGUF',
|
|
||||||
extensions: ['gguf'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
// If the dialog returns a file path, extract just the file name
|
|
||||||
const fileName =
|
|
||||||
typeof selectedFile === 'string'
|
|
||||||
? selectedFile.split(/[\\/]/).pop()
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
if (selectedFile && fileName) {
|
|
||||||
try {
|
|
||||||
await pullModel(fileName, selectedFile)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(
|
|
||||||
t('providers:importModelError'),
|
|
||||||
error
|
|
||||||
)
|
|
||||||
} finally {
|
|
||||||
// Refresh the provider to update the models list
|
|
||||||
getProviders().then(setProviders)
|
|
||||||
toast.success(t('providers:import'), {
|
|
||||||
id: `import-model-${provider.provider}`,
|
|
||||||
description: t(
|
|
||||||
'providers:importModelSuccess',
|
|
||||||
{ provider: provider.provider }
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<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">
|
<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">
|
||||||
<IconFolderPlus
|
{importingModel ? (
|
||||||
size={18}
|
<IconLoader
|
||||||
className="text-main-view-fg/50"
|
size={18}
|
||||||
/>
|
className="text-main-view-fg/50 animate-spin"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IconFolderPlus
|
||||||
|
size={18}
|
||||||
|
className="text-main-view-fg/50"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<span className="text-main-view-fg/70">
|
<span className="text-main-view-fg/70">
|
||||||
{t('providers:import')}
|
{importingModel ? 'Importing...' : t('providers:import')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user