Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
f5a6a1ab
Unverified
提交
f5a6a1ab
authored
5月 13, 2025
作者:
Will Chen
提交者:
GitHub
5月 13, 2025
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Delete custom provider (#137)
上级
ea9301c7
显示空白字符变更
内嵌
并排
正在显示
5 个修改的文件
包含
181 行增加
和
4 行删除
+181
-4
ProviderSettings.tsx
src/components/ProviderSettings.tsx
+84
-3
useCustomLanguageModelProvider.ts
src/hooks/useCustomLanguageModelProvider.ts
+24
-1
language_model_handlers.ts
src/ipc/handlers/language_model_handlers.ts
+66
-0
ipc_client.ts
src/ipc/ipc_client.ts
+6
-0
preload.ts
src/preload.ts
+1
-0
没有找到文件。
src/components/ProviderSettings.tsx
浏览文件 @
f5a6a1ab
...
@@ -9,17 +9,36 @@ import { providerSettingsRoute } from "@/routes/settings/providers/$provider";
...
@@ -9,17 +9,36 @@ import { providerSettingsRoute } from "@/routes/settings/providers/$provider";
import
type
{
LanguageModelProvider
}
from
"@/ipc/ipc_types"
;
import
type
{
LanguageModelProvider
}
from
"@/ipc/ipc_types"
;
import
{
useLanguageModelProviders
}
from
"@/hooks/useLanguageModelProviders"
;
import
{
useLanguageModelProviders
}
from
"@/hooks/useLanguageModelProviders"
;
import
{
GiftIcon
,
PlusIcon
}
from
"lucide-react"
;
import
{
useCustomLanguageModelProvider
}
from
"@/hooks/useCustomLanguageModelProvider"
;
import
{
GiftIcon
,
PlusIcon
,
MoreVertical
,
Trash2
}
from
"lucide-react"
;
import
{
Skeleton
}
from
"./ui/skeleton"
;
import
{
Skeleton
}
from
"./ui/skeleton"
;
import
{
Alert
,
AlertDescription
,
AlertTitle
}
from
"./ui/alert"
;
import
{
Alert
,
AlertDescription
,
AlertTitle
}
from
"./ui/alert"
;
import
{
AlertTriangle
}
from
"lucide-react"
;
import
{
AlertTriangle
}
from
"lucide-react"
;
import
{
useState
}
from
"react"
;
import
{
useState
}
from
"react"
;
import
{
DropdownMenu
,
DropdownMenuContent
,
DropdownMenuItem
,
DropdownMenuTrigger
,
}
from
"@/components/ui/dropdown-menu"
;
import
{
AlertDialog
,
AlertDialogAction
,
AlertDialogCancel
,
AlertDialogContent
,
AlertDialogDescription
,
AlertDialogFooter
,
AlertDialogHeader
,
AlertDialogTitle
,
}
from
"@/components/ui/alert-dialog"
;
import
{
CreateCustomProviderDialog
}
from
"./CreateCustomProviderDialog"
;
import
{
CreateCustomProviderDialog
}
from
"./CreateCustomProviderDialog"
;
export
function
ProviderSettingsGrid
()
{
export
function
ProviderSettingsGrid
()
{
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
const
[
isDialogOpen
,
setIsDialogOpen
]
=
useState
(
false
);
const
[
isDialogOpen
,
setIsDialogOpen
]
=
useState
(
false
);
const
[
providerToDelete
,
setProviderToDelete
]
=
useState
<
string
|
null
>
(
null
);
const
{
const
{
data
:
providers
,
data
:
providers
,
...
@@ -29,6 +48,8 @@ export function ProviderSettingsGrid() {
...
@@ -29,6 +48,8 @@ export function ProviderSettingsGrid() {
refetch
,
refetch
,
}
=
useLanguageModelProviders
();
}
=
useLanguageModelProviders
();
const
{
deleteProvider
,
isDeleting
}
=
useCustomLanguageModelProvider
();
const
handleProviderClick
=
(
providerId
:
string
)
=>
{
const
handleProviderClick
=
(
providerId
:
string
)
=>
{
navigate
({
navigate
({
to
:
providerSettingsRoute
.
id
,
to
:
providerSettingsRoute
.
id
,
...
@@ -36,6 +57,14 @@ export function ProviderSettingsGrid() {
...
@@ -36,6 +57,14 @@ export function ProviderSettingsGrid() {
});
});
};
};
const
handleDeleteProvider
=
async
()
=>
{
if
(
providerToDelete
)
{
await
deleteProvider
(
providerToDelete
);
setProviderToDelete
(
null
);
refetch
();
}
};
if
(
isLoading
)
{
if
(
isLoading
)
{
return
(
return
(
<
div
className=
"p-6"
>
<
div
className=
"p-6"
>
...
@@ -74,13 +103,17 @@ export function ProviderSettingsGrid() {
...
@@ -74,13 +103,17 @@ export function ProviderSettingsGrid() {
<
h2
className=
"text-2xl font-bold mb-6"
>
AI Providers
</
h2
>
<
h2
className=
"text-2xl font-bold mb-6"
>
AI Providers
</
h2
>
<
div
className=
"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
>
<
div
className=
"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
>
{
providers
?.
map
((
provider
:
LanguageModelProvider
)
=>
{
{
providers
?.
map
((
provider
:
LanguageModelProvider
)
=>
{
const
isCustom
=
provider
.
type
===
"custom"
;
return
(
return
(
<
Card
<
Card
key=
{
provider
.
id
}
key=
{
provider
.
id
}
className=
"cursor-pointer transition-all hover:shadow-md border-border"
className=
"relative transition-all hover:shadow-md border-border"
>
<
CardHeader
className=
"p-4 cursor-pointer"
onClick=
{
()
=>
handleProviderClick
(
provider
.
id
)
}
onClick=
{
()
=>
handleProviderClick
(
provider
.
id
)
}
>
>
<
CardHeader
className=
"p-4"
>
<
CardTitle
className=
"text-xl flex items-center justify-between"
>
<
CardTitle
className=
"text-xl flex items-center justify-between"
>
{
provider
.
name
}
{
provider
.
name
}
{
isProviderSetup
(
provider
.
id
)
?
(
{
isProviderSetup
(
provider
.
id
)
?
(
...
@@ -102,6 +135,30 @@ export function ProviderSettingsGrid() {
...
@@ -102,6 +135,30 @@ export function ProviderSettingsGrid() {
)
}
)
}
</
CardDescription
>
</
CardDescription
>
</
CardHeader
>
</
CardHeader
>
{
isCustom
&&
(
<
div
className=
"absolute top-2 right-2"
onClick=
{
(
e
)
=>
e
.
stopPropagation
()
}
>
<
DropdownMenu
>
<
DropdownMenuTrigger
className=
"focus:outline-none"
>
<
div
className=
"p-1 hover:bg-muted rounded-full"
>
<
MoreVertical
className=
"h-4 w-4 text-muted-foreground"
/>
</
div
>
</
DropdownMenuTrigger
>
<
DropdownMenuContent
align=
"end"
>
<
DropdownMenuItem
className=
"text-destructive focus:text-destructive"
onClick=
{
()
=>
setProviderToDelete
(
provider
.
id
)
}
>
<
Trash2
className=
"h-4 w-4 mr-2"
/>
Delete Provider
</
DropdownMenuItem
>
</
DropdownMenuContent
>
</
DropdownMenu
>
</
div
>
)
}
</
Card
>
</
Card
>
);
);
})
}
})
}
...
@@ -131,6 +188,30 @@ export function ProviderSettingsGrid() {
...
@@ -131,6 +188,30 @@ export function ProviderSettingsGrid() {
refetch
();
refetch
();
}
}
}
}
/>
/>
<
AlertDialog
open=
{
!!
providerToDelete
}
onOpenChange=
{
(
open
)
=>
!
open
&&
setProviderToDelete
(
null
)
}
>
<
AlertDialogContent
>
<
AlertDialogHeader
>
<
AlertDialogTitle
>
Delete Custom Provider
</
AlertDialogTitle
>
<
AlertDialogDescription
>
This will permanently delete this custom provider and all its
associated models. This action cannot be undone.
</
AlertDialogDescription
>
</
AlertDialogHeader
>
<
AlertDialogFooter
>
<
AlertDialogCancel
disabled=
{
isDeleting
}
>
Cancel
</
AlertDialogCancel
>
<
AlertDialogAction
onClick=
{
handleDeleteProvider
}
disabled=
{
isDeleting
}
>
{
isDeleting
?
"Deleting..."
:
"Delete Provider"
}
</
AlertDialogAction
>
</
AlertDialogFooter
>
</
AlertDialogContent
>
</
AlertDialog
>
</
div
>
</
div
>
);
);
}
}
src/hooks/useCustomLanguageModelProvider.ts
浏览文件 @
f5a6a1ab
...
@@ -40,15 +40,38 @@ export function useCustomLanguageModelProvider() {
...
@@ -40,15 +40,38 @@ export function useCustomLanguageModelProvider() {
},
},
});
});
const
deleteProviderMutation
=
useMutation
({
mutationFn
:
async
(
providerId
:
string
):
Promise
<
void
>
=>
{
if
(
!
providerId
)
{
throw
new
Error
(
"Provider ID is required"
);
}
return
ipcClient
.
deleteCustomLanguageModelProvider
(
providerId
);
},
onSuccess
:
()
=>
{
// Invalidate and refetch
queryClient
.
invalidateQueries
({
queryKey
:
[
"languageModelProviders"
]
});
},
onError
:
(
error
)
=>
{
showError
(
error
);
},
});
const
createProvider
=
async
(
const
createProvider
=
async
(
params
:
CreateCustomLanguageModelProviderParams
,
params
:
CreateCustomLanguageModelProviderParams
,
):
Promise
<
LanguageModelProvider
>
=>
{
):
Promise
<
LanguageModelProvider
>
=>
{
return
createProviderMutation
.
mutateAsync
(
params
);
return
createProviderMutation
.
mutateAsync
(
params
);
};
};
const
deleteProvider
=
async
(
providerId
:
string
):
Promise
<
void
>
=>
{
return
deleteProviderMutation
.
mutateAsync
(
providerId
);
};
return
{
return
{
createProvider
,
createProvider
,
deleteProvider
,
isCreating
:
createProviderMutation
.
isPending
,
isCreating
:
createProviderMutation
.
isPending
,
error
:
createProviderMutation
.
error
,
isDeleting
:
deleteProviderMutation
.
isPending
,
error
:
createProviderMutation
.
error
||
deleteProviderMutation
.
error
,
};
};
}
}
src/ipc/handlers/language_model_handlers.ts
浏览文件 @
f5a6a1ab
...
@@ -204,6 +204,72 @@ export function registerLanguageModelHandlers() {
...
@@ -204,6 +204,72 @@ export function registerLanguageModelHandlers() {
},
},
);
);
handle
(
"delete-custom-language-model-provider"
,
async
(
event
:
IpcMainInvokeEvent
,
params
:
{
providerId
:
string
},
):
Promise
<
void
>
=>
{
const
{
providerId
}
=
params
;
// Validation
if
(
!
providerId
)
{
throw
new
Error
(
"Provider ID is required"
);
}
logger
.
info
(
`Handling delete-custom-language-model-provider for providerId:
${
providerId
}
`
,
);
// Check if the provider exists before attempting deletion
const
existingProvider
=
await
db
.
select
({
id
:
languageModelProvidersSchema
.
id
})
.
from
(
languageModelProvidersSchema
)
.
where
(
eq
(
languageModelProvidersSchema
.
id
,
providerId
))
.
get
();
if
(
!
existingProvider
)
{
// If the provider doesn't exist, maybe it was already deleted. Log and return.
logger
.
warn
(
`Provider with ID "
${
providerId
}
" not found. It might have been deleted already.`
,
);
// Optionally, throw new Error(`Provider with ID "${providerId}" not found`);
// Deciding to return gracefully instead of throwing an error if not found.
return
;
}
// Use a transaction to ensure atomicity
await
db
.
transaction
(
async
(
tx
)
=>
{
// 1. Delete associated models
const
deleteModelsResult
=
await
tx
.
delete
(
languageModelsSchema
)
.
where
(
eq
(
languageModelsSchema
.
provider_id
,
providerId
))
.
run
();
logger
.
info
(
`Deleted
${
deleteModelsResult
.
changes
}
model(s) associated with provider
${
providerId
}
`
,
);
// 2. Delete the provider
const
deleteProviderResult
=
await
tx
.
delete
(
languageModelProvidersSchema
)
.
where
(
eq
(
languageModelProvidersSchema
.
id
,
providerId
))
.
run
();
if
(
deleteProviderResult
.
changes
===
0
)
{
// This case should ideally not happen if existingProvider check passed,
// but adding safety check within transaction.
logger
.
error
(
`Failed to delete provider with ID "
${
providerId
}
" during transaction, although it was found initially. Rolling back.`
,
);
throw
new
Error
(
`Failed to delete provider with ID "
${
providerId
}
" which should have existed.`
,
);
}
logger
.
info
(
`Successfully deleted provider with ID "
${
providerId
}
".`
);
});
},
);
handle
(
handle
(
"get-language-models"
,
"get-language-models"
,
async
(
async
(
...
...
src/ipc/ipc_client.ts
浏览文件 @
f5a6a1ab
...
@@ -774,6 +774,12 @@ export class IpcClient {
...
@@ -774,6 +774,12 @@ export class IpcClient {
return
this
.
ipcRenderer
.
invoke
(
"delete-custom-model"
,
params
);
return
this
.
ipcRenderer
.
invoke
(
"delete-custom-model"
,
params
);
}
}
async
deleteCustomLanguageModelProvider
(
providerId
:
string
):
Promise
<
void
>
{
return
this
.
ipcRenderer
.
invoke
(
"delete-custom-language-model-provider"
,
{
providerId
,
});
}
// --- End window control methods ---
// --- End window control methods ---
// --- Language Model Operations ---
// --- Language Model Operations ---
...
...
src/preload.ts
浏览文件 @
f5a6a1ab
...
@@ -8,6 +8,7 @@ const validInvokeChannels = [
...
@@ -8,6 +8,7 @@ const validInvokeChannels = [
"get-language-models"
,
"get-language-models"
,
"create-custom-language-model"
,
"create-custom-language-model"
,
"get-language-model-providers"
,
"get-language-model-providers"
,
"delete-custom-language-model-provider"
,
"create-custom-language-model-provider"
,
"create-custom-language-model-provider"
,
"delete-custom-language-model"
,
"delete-custom-language-model"
,
"delete-custom-model"
,
"delete-custom-model"
,
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论