## Summary Adds the ability to edit existing custom AI providers through the settings UI. ## Changes Made - **UI Changes:** - Added "Edit Provider" button to custom provider dropdown menu - Modified `CreateCustomProviderDialog` to support edit mode - **Backend Changes:** - Implemented `editCustomLanguageModelProvider` handler in `language_model_handlers.ts` - Added corresponding IPC client method - Database transaction ensures atomicity when updating provider and associated models - **Testing:** - Added comprehensive e2e test covering edit functionality - Tests verify form pre-population, field updates, and UI persistence https://github.com/user-attachments/assets/e8c8600e-4fb7-4816-be95-993ede1224d4 ## Closes Fixes #1232 and #1171 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds edit support for custom language model providers in Settings. Users can update provider ID, name, API base URL, and API key env var, with safe backend updates that also retarget associated models if the ID changes. - New Features - Added “Edit Provider” option in the custom provider menu. - Dialog supports edit mode with pre-filled fields, unified loading state, and update button text. - New IPC handler to edit providers with validation and a transaction; updates linked models when IDs change. - IPC client and preload channel updated; React hook exposes editProvider mutation with cache invalidation. - Added e2e test covering the full edit flow. <!-- End of auto-generated description by cubic. -->
This commit is contained in:
committed by
GitHub
parent
decd05e764
commit
8c3fdb0ad0
26
e2e-tests/edit_provider.spec.ts
Normal file
26
e2e-tests/edit_provider.spec.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { test } from "./helpers/test_helper";
|
||||||
|
|
||||||
|
test("can edit custom provider", async ({ po }) => {
|
||||||
|
await po.setUp();
|
||||||
|
await po.goToSettingsTab();
|
||||||
|
|
||||||
|
// Create a provider first
|
||||||
|
|
||||||
|
// Edit it
|
||||||
|
await po.page.getByTestId("custom-provider-more-options").click();
|
||||||
|
await po.page.getByRole("button", { name: "Edit Provider" }).click();
|
||||||
|
|
||||||
|
await po.page.getByRole("textbox", { name: "Display Name" }).clear();
|
||||||
|
await po.page
|
||||||
|
.getByRole("textbox", { name: "Display Name" })
|
||||||
|
.fill("Updated Test Provider");
|
||||||
|
|
||||||
|
await po.page.getByRole("textbox", { name: "API Base URL" }).clear();
|
||||||
|
await po.page
|
||||||
|
.getByRole("textbox", { name: "API Base URL" })
|
||||||
|
.fill("https://api.updated-test.com/v1");
|
||||||
|
|
||||||
|
await po.page.getByRole("button", { name: "Update Provider" }).click();
|
||||||
|
|
||||||
|
// Make sure UI hasn't freezed
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -11,38 +11,73 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import { useCustomLanguageModelProvider } from "@/hooks/useCustomLanguageModelProvider";
|
import { useCustomLanguageModelProvider } from "@/hooks/useCustomLanguageModelProvider";
|
||||||
|
import type { LanguageModelProvider } from "@/ipc/ipc_types";
|
||||||
|
|
||||||
interface CreateCustomProviderDialogProps {
|
interface CreateCustomProviderDialogProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
|
editingProvider?: LanguageModelProvider | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CreateCustomProviderDialog({
|
export function CreateCustomProviderDialog({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
|
editingProvider = null,
|
||||||
}: CreateCustomProviderDialogProps) {
|
}: CreateCustomProviderDialogProps) {
|
||||||
const [id, setId] = useState("");
|
const [id, setId] = useState("");
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [apiBaseUrl, setApiBaseUrl] = useState("");
|
const [apiBaseUrl, setApiBaseUrl] = useState("");
|
||||||
const [envVarName, setEnvVarName] = useState("");
|
const [envVarName, setEnvVarName] = useState("");
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
|
const isEditMode = Boolean(editingProvider);
|
||||||
|
|
||||||
const { createProvider, isCreating, error } =
|
const { createProvider, editProvider, isCreating, isEditing, error } =
|
||||||
useCustomLanguageModelProvider();
|
useCustomLanguageModelProvider();
|
||||||
|
// Load provider data when editing
|
||||||
|
useEffect(() => {
|
||||||
|
if (editingProvider && isOpen) {
|
||||||
|
const cleanId = editingProvider.id?.startsWith("custom::")
|
||||||
|
? editingProvider.id.replace("custom::", "")
|
||||||
|
: editingProvider.id || "";
|
||||||
|
setId(cleanId);
|
||||||
|
setName(editingProvider.name || "");
|
||||||
|
setApiBaseUrl(editingProvider.apiBaseUrl || "");
|
||||||
|
setEnvVarName(editingProvider.envVarName || "");
|
||||||
|
} else if (!isOpen) {
|
||||||
|
// Reset form when dialog closes
|
||||||
|
setId("");
|
||||||
|
setName("");
|
||||||
|
setApiBaseUrl("");
|
||||||
|
setEnvVarName("");
|
||||||
|
setErrorMessage("");
|
||||||
|
}
|
||||||
|
}, [editingProvider, isOpen]);
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setErrorMessage("");
|
setErrorMessage("");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createProvider({
|
if (isEditMode && editingProvider) {
|
||||||
id: id.trim(),
|
const cleanId = editingProvider.id?.startsWith("custom::")
|
||||||
name: name.trim(),
|
? editingProvider.id.replace("custom::", "")
|
||||||
apiBaseUrl: apiBaseUrl.trim(),
|
: editingProvider.id || "";
|
||||||
envVarName: envVarName.trim() || undefined,
|
await editProvider({
|
||||||
});
|
id: cleanId,
|
||||||
|
name: name.trim(),
|
||||||
|
apiBaseUrl: apiBaseUrl.trim(),
|
||||||
|
envVarName: envVarName.trim() || undefined,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await createProvider({
|
||||||
|
id: id.trim(),
|
||||||
|
name: name.trim(),
|
||||||
|
apiBaseUrl: apiBaseUrl.trim(),
|
||||||
|
envVarName: envVarName.trim() || undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Reset form
|
// Reset form
|
||||||
setId("");
|
setId("");
|
||||||
@@ -55,25 +90,30 @@ export function CreateCustomProviderDialog({
|
|||||||
setErrorMessage(
|
setErrorMessage(
|
||||||
error instanceof Error
|
error instanceof Error
|
||||||
? error.message
|
? error.message
|
||||||
: "Failed to create custom provider",
|
: `Failed to ${isEditMode ? "edit" : "create"} custom provider`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
if (!isCreating) {
|
if (!isCreating && !isEditing) {
|
||||||
setErrorMessage("");
|
setErrorMessage("");
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const isLoading = isCreating || isEditing;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||||
<DialogContent className="sm:max-w-md">
|
<DialogContent className="sm:max-w-md">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Add Custom Provider</DialogTitle>
|
<DialogTitle>
|
||||||
|
{isEditMode ? "Edit Custom Provider" : "Add Custom Provider"}
|
||||||
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Connect to a custom language model provider API
|
{isEditMode
|
||||||
|
? "Update your custom language model provider configuration."
|
||||||
|
: "Connect to a custom language model provider API."}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
@@ -86,7 +126,7 @@ export function CreateCustomProviderDialog({
|
|||||||
onChange={(e) => setId(e.target.value)}
|
onChange={(e) => setId(e.target.value)}
|
||||||
placeholder="E.g., my-provider"
|
placeholder="E.g., my-provider"
|
||||||
required
|
required
|
||||||
disabled={isCreating}
|
disabled={isLoading || isEditMode}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
A unique identifier for this provider (no spaces).
|
A unique identifier for this provider (no spaces).
|
||||||
@@ -101,7 +141,7 @@ export function CreateCustomProviderDialog({
|
|||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
placeholder="E.g., My Provider"
|
placeholder="E.g., My Provider"
|
||||||
required
|
required
|
||||||
disabled={isCreating}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
The name that will be displayed in the UI.
|
The name that will be displayed in the UI.
|
||||||
@@ -116,7 +156,7 @@ export function CreateCustomProviderDialog({
|
|||||||
onChange={(e) => setApiBaseUrl(e.target.value)}
|
onChange={(e) => setApiBaseUrl(e.target.value)}
|
||||||
placeholder="E.g., https://api.example.com/v1"
|
placeholder="E.g., https://api.example.com/v1"
|
||||||
required
|
required
|
||||||
disabled={isCreating}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
The base URL for the API endpoint.
|
The base URL for the API endpoint.
|
||||||
@@ -130,7 +170,7 @@ export function CreateCustomProviderDialog({
|
|||||||
value={envVarName}
|
value={envVarName}
|
||||||
onChange={(e) => setEnvVarName(e.target.value)}
|
onChange={(e) => setEnvVarName(e.target.value)}
|
||||||
placeholder="E.g., MY_PROVIDER_API_KEY"
|
placeholder="E.g., MY_PROVIDER_API_KEY"
|
||||||
disabled={isCreating}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Environment variable name for the API key.
|
Environment variable name for the API key.
|
||||||
@@ -151,13 +191,19 @@ export function CreateCustomProviderDialog({
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
disabled={isCreating}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" disabled={isCreating}>
|
<Button type="submit" disabled={isLoading}>
|
||||||
{isCreating && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||||
{isCreating ? "Adding..." : "Add Provider"}
|
{isLoading
|
||||||
|
? isEditMode
|
||||||
|
? "Updating..."
|
||||||
|
: "Adding..."
|
||||||
|
: isEditMode
|
||||||
|
? "Update Provider"
|
||||||
|
: "Add Provider"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type { LanguageModelProvider } from "@/ipc/ipc_types";
|
|||||||
|
|
||||||
import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
|
import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
|
||||||
import { useCustomLanguageModelProvider } from "@/hooks/useCustomLanguageModelProvider";
|
import { useCustomLanguageModelProvider } from "@/hooks/useCustomLanguageModelProvider";
|
||||||
import { GiftIcon, PlusIcon, MoreVertical, Trash2 } from "lucide-react";
|
import { GiftIcon, PlusIcon, MoreVertical, Trash2, Edit } 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";
|
||||||
@@ -38,6 +38,8 @@ 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 [editingProvider, setEditingProvider] =
|
||||||
|
useState<LanguageModelProvider | null>(null);
|
||||||
const [providerToDelete, setProviderToDelete] = useState<string | null>(null);
|
const [providerToDelete, setProviderToDelete] = useState<string | null>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -65,6 +67,11 @@ export function ProviderSettingsGrid() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEditProvider = (provider: LanguageModelProvider) => {
|
||||||
|
setEditingProvider(provider);
|
||||||
|
setIsDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
@@ -116,7 +123,7 @@ export function ProviderSettingsGrid() {
|
|||||||
className="p-4 cursor-pointer"
|
className="p-4 cursor-pointer"
|
||||||
onClick={() => handleProviderClick(provider.id)}
|
onClick={() => handleProviderClick(provider.id)}
|
||||||
>
|
>
|
||||||
<CardTitle className="text-lg font-medium flex items-center justify-between">
|
<CardTitle className="text-lg font-medium flex items-center justify-between mr-5">
|
||||||
{provider.name}
|
{provider.name}
|
||||||
{isProviderSetup(provider.id) ? (
|
{isProviderSetup(provider.id) ? (
|
||||||
<span className="ml-3 text-sm font-medium text-green-500 bg-green-50 dark:bg-green-900/30 border border-green-500/50 dark:border-green-500/50 px-2 py-1 rounded-full">
|
<span className="ml-3 text-sm font-medium text-green-500 bg-green-50 dark:bg-green-900/30 border border-green-500/50 dark:border-green-500/50 px-2 py-1 rounded-full">
|
||||||
@@ -140,7 +147,7 @@ export function ProviderSettingsGrid() {
|
|||||||
|
|
||||||
{isCustom && (
|
{isCustom && (
|
||||||
<div
|
<div
|
||||||
className="absolute top-2 right-2"
|
className="absolute top-2 right-0"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<Popover>
|
<Popover>
|
||||||
@@ -155,6 +162,15 @@ export function ProviderSettingsGrid() {
|
|||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent align="end" className="w-48 p-2">
|
<PopoverContent align="end" className="w-48 p-2">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="w-full justify-start mb-1"
|
||||||
|
onClick={() => handleEditProvider(provider)}
|
||||||
|
>
|
||||||
|
<Edit className="h-4 w-4 mr-2" />
|
||||||
|
Edit Provider
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -191,11 +207,16 @@ export function ProviderSettingsGrid() {
|
|||||||
|
|
||||||
<CreateCustomProviderDialog
|
<CreateCustomProviderDialog
|
||||||
isOpen={isDialogOpen}
|
isOpen={isDialogOpen}
|
||||||
onClose={() => setIsDialogOpen(false)}
|
onClose={() => {
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
setEditingProvider(null);
|
||||||
|
}}
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
setIsDialogOpen(false);
|
setIsDialogOpen(false);
|
||||||
refetch();
|
refetch();
|
||||||
|
setEditingProvider(null);
|
||||||
}}
|
}}
|
||||||
|
editingProvider={editingProvider}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AlertDialog
|
<AlertDialog
|
||||||
|
|||||||
@@ -40,6 +40,36 @@ export function useCustomLanguageModelProvider() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const editProviderMutation = useMutation({
|
||||||
|
mutationFn: async (
|
||||||
|
params: CreateCustomLanguageModelProviderParams,
|
||||||
|
): Promise<LanguageModelProvider> => {
|
||||||
|
if (!params.id.trim()) {
|
||||||
|
throw new Error("Provider ID is required");
|
||||||
|
}
|
||||||
|
if (!params.name.trim()) {
|
||||||
|
throw new Error("Provider name is required");
|
||||||
|
}
|
||||||
|
if (!params.apiBaseUrl.trim()) {
|
||||||
|
throw new Error("API base URL is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipcClient.editCustomLanguageModelProvider({
|
||||||
|
id: params.id.trim(),
|
||||||
|
name: params.name.trim(),
|
||||||
|
apiBaseUrl: params.apiBaseUrl.trim(),
|
||||||
|
envVarName: params.envVarName?.trim() || undefined,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate and refetch
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["languageModelProviders"] });
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
showError(error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const deleteProviderMutation = useMutation({
|
const deleteProviderMutation = useMutation({
|
||||||
mutationFn: async (providerId: string): Promise<void> => {
|
mutationFn: async (providerId: string): Promise<void> => {
|
||||||
if (!providerId) {
|
if (!providerId) {
|
||||||
@@ -63,15 +93,26 @@ export function useCustomLanguageModelProvider() {
|
|||||||
return createProviderMutation.mutateAsync(params);
|
return createProviderMutation.mutateAsync(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const editProvider = async (
|
||||||
|
params: CreateCustomLanguageModelProviderParams,
|
||||||
|
): Promise<LanguageModelProvider> => {
|
||||||
|
return editProviderMutation.mutateAsync(params);
|
||||||
|
};
|
||||||
|
|
||||||
const deleteProvider = async (providerId: string): Promise<void> => {
|
const deleteProvider = async (providerId: string): Promise<void> => {
|
||||||
return deleteProviderMutation.mutateAsync(providerId);
|
return deleteProviderMutation.mutateAsync(providerId);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createProvider,
|
createProvider,
|
||||||
|
editProvider,
|
||||||
deleteProvider,
|
deleteProvider,
|
||||||
isCreating: createProviderMutation.isPending,
|
isCreating: createProviderMutation.isPending,
|
||||||
|
isEditing: editProviderMutation.isPending,
|
||||||
isDeleting: deleteProviderMutation.isPending,
|
isDeleting: deleteProviderMutation.isPending,
|
||||||
error: createProviderMutation.error || deleteProviderMutation.error,
|
error:
|
||||||
|
createProviderMutation.error ||
|
||||||
|
editProviderMutation.error ||
|
||||||
|
deleteProviderMutation.error,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,67 @@ export function registerLanguageModelHandlers() {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
handle(
|
||||||
|
"edit-custom-language-model-provider",
|
||||||
|
async (
|
||||||
|
event: IpcMainInvokeEvent,
|
||||||
|
params: CreateCustomLanguageModelProviderParams,
|
||||||
|
): Promise<LanguageModelProvider> => {
|
||||||
|
const { id, name, apiBaseUrl, envVarName } = params;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
throw new Error("Provider ID is required");
|
||||||
|
}
|
||||||
|
if (!name) {
|
||||||
|
throw new Error("Provider name is required");
|
||||||
|
}
|
||||||
|
if (!apiBaseUrl) {
|
||||||
|
throw new Error("API base URL is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the provider being edited exists
|
||||||
|
const existingProvider = db
|
||||||
|
.select()
|
||||||
|
.from(languageModelProvidersSchema)
|
||||||
|
.where(eq(languageModelProvidersSchema.id, CUSTOM_PROVIDER_PREFIX + id))
|
||||||
|
.get();
|
||||||
|
|
||||||
|
if (!existingProvider) {
|
||||||
|
throw new Error(`Provider with ID "${id}" not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use transaction to ensure atomicity when updating provider and potentially its models
|
||||||
|
const result = db.transaction((tx) => {
|
||||||
|
// Update the provider
|
||||||
|
const updateResult = tx
|
||||||
|
.update(languageModelProvidersSchema)
|
||||||
|
.set({
|
||||||
|
id: CUSTOM_PROVIDER_PREFIX + id,
|
||||||
|
name,
|
||||||
|
api_base_url: apiBaseUrl,
|
||||||
|
env_var_name: envVarName || null,
|
||||||
|
})
|
||||||
|
.where(
|
||||||
|
eq(languageModelProvidersSchema.id, CUSTOM_PROVIDER_PREFIX + id),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
if (updateResult.changes === 0) {
|
||||||
|
throw new Error(`Failed to update provider with ID "${id}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
apiBaseUrl,
|
||||||
|
envVarName,
|
||||||
|
type: "custom" as const,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
logger.info(`Successfully updated provider`);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
handle(
|
handle(
|
||||||
"delete-custom-language-model",
|
"delete-custom-language-model",
|
||||||
|
|||||||
@@ -1032,6 +1032,14 @@ export class IpcClient {
|
|||||||
envVarName,
|
envVarName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public async editCustomLanguageModelProvider(
|
||||||
|
params: CreateCustomLanguageModelProviderParams,
|
||||||
|
): Promise<LanguageModelProvider> {
|
||||||
|
return this.ipcRenderer.invoke(
|
||||||
|
"edit-custom-language-model-provider",
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public async createCustomLanguageModel(
|
public async createCustomLanguageModel(
|
||||||
params: CreateCustomLanguageModelParams,
|
params: CreateCustomLanguageModelParams,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const validInvokeChannels = [
|
|||||||
"get-language-model-providers",
|
"get-language-model-providers",
|
||||||
"delete-custom-language-model-provider",
|
"delete-custom-language-model-provider",
|
||||||
"create-custom-language-model-provider",
|
"create-custom-language-model-provider",
|
||||||
|
"edit-custom-language-model-provider",
|
||||||
"delete-custom-language-model",
|
"delete-custom-language-model",
|
||||||
"delete-custom-model",
|
"delete-custom-model",
|
||||||
"chat:add-dep",
|
"chat:add-dep",
|
||||||
|
|||||||
Reference in New Issue
Block a user