diff --git a/ui/desktop/src/components/Modal.tsx b/ui/desktop/src/components/Modal.tsx index 0a6a2374ab2a..b8cd56659c31 100644 --- a/ui/desktop/src/components/Modal.tsx +++ b/ui/desktop/src/components/Modal.tsx @@ -53,11 +53,13 @@ export default function Modal({ > -
{children}
+
{children}
{footer && ( -
{footer}
+
+ {footer} +
)}
diff --git a/ui/desktop/src/components/settings_v2/SettingsView.tsx b/ui/desktop/src/components/settings_v2/SettingsView.tsx index 935d5dd052e3..07a017c1d988 100644 --- a/ui/desktop/src/components/settings_v2/SettingsView.tsx +++ b/ui/desktop/src/components/settings_v2/SettingsView.tsx @@ -6,6 +6,7 @@ import { useConfig } from '../ConfigContext'; import { Button } from '../ui/button'; import { Plus, Sliders } from 'lucide-react'; import ExtensionsSection from './extensions/ExtensionsSection'; +import { AddModelButton } from './models/AddModelButton'; interface ModelOption { id: string; @@ -101,10 +102,7 @@ export default function SettingsView({ ))}
- + + {isAddModelModalOpen ? setIsAddModelModalOpen(false)} /> : null} + + ); +}; diff --git a/ui/desktop/src/components/settings_v2/models/AddModelModal.tsx b/ui/desktop/src/components/settings_v2/models/AddModelModal.tsx new file mode 100644 index 000000000000..0ff570a19b58 --- /dev/null +++ b/ui/desktop/src/components/settings_v2/models/AddModelModal.tsx @@ -0,0 +1,116 @@ +import React, { useEffect, useState } from 'react'; +import { ExternalLink, Plus } from 'lucide-react'; + +import Modal from '../../Modal'; +import { Button } from '../../ui/button'; +import { QUICKSTART_GUIDE_URL } from '../providers/modal/constants'; +import { Input } from '../../ui/input'; +import { Select } from '../../ui/Select'; +import { useConfig } from '../../ConfigContext'; +import { ToastFailureGeneral, ToastSuccessModelSwitch } from '../../settings/models/toasts'; +import { initializeSystem } from '../../../../src/utils/providerUtils'; + +const ModalButtons = ({ onSubmit, onCancel }) => ( +
+ + +
+); + +type AddModelModalProps = { onClose: () => void }; +export const AddModelModal = ({ onClose }: AddModelModalProps) => { + const { getProviders, upsert } = useConfig(); + const [providerOptions, setProviderOptions] = useState([]); + const [provider, setProvider] = useState(null); + const [modelName, setModelName] = useState(''); + + const changeModel = async () => { + try { + await upsert('GOOSE_PROVIDER', provider, false); + await upsert('GOOSE_MODEL', modelName, false); + await initializeSystem(provider, modelName); + ToastSuccessModelSwitch({ provider, name: modelName }); + onClose(); + } catch (e) { + ToastFailureGeneral(e.message); + } + }; + + useEffect(() => { + (async () => { + try { + const providersResponse = await getProviders(false); + const activeProviders = providersResponse.filter((provider) => provider.is_configured); + setProviderOptions( + activeProviders.map(({ metadata, name }) => ({ + value: name, + label: metadata.display_name, + })) + ); + } catch (error) { + console.error('Failed to load providers:', error); + } + })(); + }, [getProviders]); + + return ( +
+ }> +
+
+ +
Add model
+
+ Configure your AI model providers by adding their API keys. your Keys are stored + securely and encrypted locally. +
+ +
+ +
+ setModelName(event.target.value)} + value={modelName} + /> +
+
+
+
+ ); +}; diff --git a/ui/desktop/src/components/ui/Select.tsx b/ui/desktop/src/components/ui/Select.tsx new file mode 100644 index 000000000000..67d526e0ddfe --- /dev/null +++ b/ui/desktop/src/components/ui/Select.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import ReactSelect from 'react-select'; + +export const Select = (props) => { + return ( + 'w-full cursor-pointer', + indicatorSeparator: () => 'h-0', + control: ({ isFocused }) => + `border-2 ${isFocused ? 'border-borderStandard' : 'border-borderSubtle'} focus:border-borderStandard hover:border-borderStandard rounded-md w-full px-4 py-2.5 text-sm text-textSubtle`, + menu: () => 'mt-3 bg-bgStandard shadow-xl rounded-md text-textSubtle overflow-hidden', + option: () => + 'py-4 px-4 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 text-textProminent font-medium', + }} + /> + ); +};