diff --git a/ui/litellm-dashboard/src/components/add_model/add_model_tab.tsx b/ui/litellm-dashboard/src/components/add_model/add_model_tab.tsx index 35cd94c080..c699c161e3 100644 --- a/ui/litellm-dashboard/src/components/add_model/add_model_tab.tsx +++ b/ui/litellm-dashboard/src/components/add_model/add_model_tab.tsx @@ -148,12 +148,13 @@ const AddModelTab: React.FC = ({ - prevValues.credential_name !== currentValues.credential_name || + prevValues.litellm_credential_name !== currentValues.litellm_credential_name || prevValues.provider !== currentValues.provider } > {({ getFieldValue }) => { const credentialName = getFieldValue('litellm_credential_name'); + console.log("🔑 Credential Name Changed:", credentialName); // Only show provider specific fields if no credentials selected if (!credentialName) { return ( diff --git a/ui/litellm-dashboard/src/components/model_add/add_credentials_tab.tsx b/ui/litellm-dashboard/src/components/model_add/add_credentials_tab.tsx index 14ec54950c..1e06039c0d 100644 --- a/ui/litellm-dashboard/src/components/model_add/add_credentials_tab.tsx +++ b/ui/litellm-dashboard/src/components/model_add/add_credentials_tab.tsx @@ -15,33 +15,46 @@ import { Providers, providerLogoMap } from "../provider_info_helpers"; import type { FormInstance } from "antd"; import ProviderSpecificFields from "../add_model/provider_specific_fields"; import { TextInput } from "@tremor/react"; +import { CredentialItem } from "../networking"; const { Title, Link } = Typography; interface AddCredentialsModalProps { isVisible: boolean; onCancel: () => void; onAddCredential: (values: any) => void; + onUpdateCredential: (values: any) => void; uploadProps: UploadProps; + addOrEdit: "add" | "edit"; + existingCredential: CredentialItem | null; } const AddCredentialsModal: React.FC = ({ isVisible, onCancel, onAddCredential, - uploadProps + onUpdateCredential, + uploadProps, + addOrEdit, + existingCredential }) => { const [form] = Form.useForm(); const [selectedProvider, setSelectedProvider] = useState(Providers.OpenAI); const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); + console.log(`existingCredential in add credentials tab: ${JSON.stringify(existingCredential)}`); + const handleSubmit = (values: any) => { - onAddCredential(values); + if (addOrEdit === "add") { + onAddCredential(values); + } else { + onUpdateCredential(values); + } form.resetFields(); }; return ( { onCancel(); @@ -55,18 +68,31 @@ const AddCredentialsModal: React.FC = ({ onFinish={handleSubmit} layout="vertical" > + {/* Credential Name */} + + + + {/* Provider Selection */} { - setSelectedProvider(value); + setSelectedProvider(value as Providers); }} > {Object.entries(Providers).map(([providerEnum, providerDisplayName]) => ( @@ -97,14 +123,7 @@ const AddCredentialsModal: React.FC = ({ - {/* Credential Name */} - - - + = ({ diff --git a/ui/litellm-dashboard/src/components/model_add/credentials.tsx b/ui/litellm-dashboard/src/components/model_add/credentials.tsx index 604b93394e..7afb0235cb 100644 --- a/ui/litellm-dashboard/src/components/model_add/credentials.tsx +++ b/ui/litellm-dashboard/src/components/model_add/credentials.tsx @@ -21,7 +21,7 @@ import { } from "@heroicons/react/outline"; import { UploadProps } from "antd/es/upload"; import { PlusIcon } from "@heroicons/react/solid"; -import { credentialListCall, credentialCreateCall, credentialDeleteCall, CredentialItem, CredentialsResponse } from "@/components/networking"; // Assume this is your networking function +import { credentialListCall, credentialCreateCall, credentialDeleteCall, credentialUpdateCall, CredentialItem, CredentialsResponse } from "@/components/networking"; // Assume this is your networking function import AddCredentialsTab from "./add_credentials_tab"; import { Form, message } from "antd"; interface CredentialsPanelProps { @@ -35,7 +35,36 @@ interface CredentialsPanelProps { const CredentialsPanel: React.FC = ({ accessToken, uploadProps, credentialList, fetchCredentials }) => { const [isAddModalOpen, setIsAddModalOpen] = useState(false); + const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false); + const [selectedCredential, setSelectedCredential] = useState(null); const [form] = Form.useForm(); + console.log(`selectedCredential in credentials panel: ${JSON.stringify(selectedCredential)}`); + + const restrictedFields = ['credential_name', 'custom_llm_provider']; + const handleUpdateCredential = async (values: any) => { + if (!accessToken) { + console.error('No access token found'); + return; + } + + const filter_credential_values = Object.entries(values) + .filter(([key]) => !restrictedFields.includes(key)) + .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); + // Transform form values into credential structure + const newCredential = { + credential_name: values.credential_name, + credential_values: filter_credential_values, + credential_info: { + custom_llm_provider: values.custom_llm_provider, + } + }; + + const response = await credentialUpdateCall(accessToken, values.credential_name, newCredential); + message.success('Credential updated successfully'); + console.log(`response: ${JSON.stringify(response)}`); + setIsUpdateModalOpen(false); + fetchCredentials(accessToken); + } const handleAddCredential = async (values: any) => { if (!accessToken) { @@ -44,7 +73,7 @@ const CredentialsPanel: React.FC = ({ accessToken, upload } const filter_credential_values = Object.entries(values) - .filter(([key]) => !['credential_name', 'custom_llm_provider'].includes(key)) + .filter(([key]) => !restrictedFields.includes(key)) .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); // Transform form values into credential structure const newCredential = { @@ -143,8 +172,13 @@ const CredentialsPanel: React.FC = ({ accessToken, upload + {model.litellm_params.litellm_credential_name.slice(0, 7)}... ) : ( diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 7abbb7deb4..46099906ac 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -2648,6 +2648,51 @@ export const credentialDeleteCall = async (accessToken: String, credentialName: } }; +export const credentialUpdateCall = async ( + accessToken: string, + credentialName: string, + formValues: Record // Assuming formValues is an object +) => { + try { + console.log("Form Values in credentialUpdateCall:", formValues); // Log the form values before making the API call + if (formValues.metadata) { + console.log("formValues.metadata:", formValues.metadata); + // if there's an exception JSON.parse, show it in the message + try { + formValues.metadata = JSON.parse(formValues.metadata); + } catch (error) { + throw new Error("Failed to parse metadata: " + error); + } + } + + const url = proxyBaseUrl ? `${proxyBaseUrl}/credentials/${credentialName}` : `/credentials/${credentialName}`; + const response = await fetch(url, { + method: "PUT", + headers: { + [globalLitellmHeaderName]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + ...formValues, // Include formValues in the request body + }), + }); + + if (!response.ok) { + const errorData = await response.text(); + handleError(errorData); + console.error("Error response from the server:", errorData); + throw new Error("Network response was not ok"); + } + + const data = await response.json(); + console.log("API Response:", data); + return data; + // Handle success - you might want to update some state or UI based on the created key + } catch (error) { + console.error("Failed to create key:", error); + throw error; + } +}; export const keyUpdateCall = async ( accessToken: string,