From 839480874bc8380ec94ac32d1aa78efbdb7ad576 Mon Sep 17 00:00:00 2001 From: Matt Diamant Date: Fri, 14 Mar 2025 16:22:47 -0700 Subject: [PATCH 1/2] settings v2 add model --- ui/desktop/src/components/Modal.tsx | 8 +- .../components/settings_v2/SettingsView.tsx | 6 +- .../settings_v2/models/AddModelButton.tsx | 21 ++++ .../settings_v2/models/AddModelModal.tsx | 101 ++++++++++++++++++ ui/desktop/src/components/ui/Select.tsx | 20 ++++ 5 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 ui/desktop/src/components/settings_v2/models/AddModelButton.tsx create mode 100644 ui/desktop/src/components/settings_v2/models/AddModelModal.tsx create mode 100644 ui/desktop/src/components/ui/Select.tsx 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..a0c4e6024a5b --- /dev/null +++ b/ui/desktop/src/components/settings_v2/models/AddModelModal.tsx @@ -0,0 +1,101 @@ +import React, { 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 { useActiveKeys } from '../../settings/api_keys/ActiveKeysContext'; +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 { activeKeys } = useActiveKeys(); + const config = useConfig(); + const [provider, setProvider] = useState(null); + const [modelName, setModelName] = useState(''); + const providerOptions = activeKeys.map((key) => ({ value: key.toLowerCase(), label: key })); + + const changeModel = async () => { + try { + await config.upsert('GOOSE_PROVIDER', provider, false); + await config.upsert('GOOSE_MODEL', modelName, false); + await initializeSystem(provider, modelName); + ToastSuccessModelSwitch({ provider, name: modelName }); + onClose(); + } catch (e) { + ToastFailureGeneral(e.message); + } + }; + + 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', + }} + /> + ); +}; From 57562dd2f7295a21908de87d6288774043c91e5f Mon Sep 17 00:00:00 2001 From: Matt Diamant Date: Tue, 18 Mar 2025 10:35:52 -0700 Subject: [PATCH 2/2] use config context instead of activeKeys --- .../settings_v2/models/AddModelModal.tsx | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/ui/desktop/src/components/settings_v2/models/AddModelModal.tsx b/ui/desktop/src/components/settings_v2/models/AddModelModal.tsx index a0c4e6024a5b..0ff570a19b58 100644 --- a/ui/desktop/src/components/settings_v2/models/AddModelModal.tsx +++ b/ui/desktop/src/components/settings_v2/models/AddModelModal.tsx @@ -1,10 +1,9 @@ -import React, { useState } from 'react'; +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 { useActiveKeys } from '../../settings/api_keys/ActiveKeysContext'; import { Input } from '../../ui/input'; import { Select } from '../../ui/Select'; import { useConfig } from '../../ConfigContext'; @@ -34,16 +33,15 @@ const ModalButtons = ({ onSubmit, onCancel }) => ( type AddModelModalProps = { onClose: () => void }; export const AddModelModal = ({ onClose }: AddModelModalProps) => { - const { activeKeys } = useActiveKeys(); - const config = useConfig(); + const { getProviders, upsert } = useConfig(); + const [providerOptions, setProviderOptions] = useState([]); const [provider, setProvider] = useState(null); const [modelName, setModelName] = useState(''); - const providerOptions = activeKeys.map((key) => ({ value: key.toLowerCase(), label: key })); const changeModel = async () => { try { - await config.upsert('GOOSE_PROVIDER', provider, false); - await config.upsert('GOOSE_MODEL', modelName, false); + await upsert('GOOSE_PROVIDER', provider, false); + await upsert('GOOSE_MODEL', modelName, false); await initializeSystem(provider, modelName); ToastSuccessModelSwitch({ provider, name: modelName }); onClose(); @@ -52,6 +50,23 @@ export const AddModelModal = ({ onClose }: AddModelModalProps) => { } }; + 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 (
}>