From f8205375fe6085a9679361345dde853069569432 Mon Sep 17 00:00:00 2001 From: Lily Delalande Date: Tue, 25 Mar 2025 15:43:16 -0400 Subject: [PATCH 1/7] pulled main --- .../src/components/settings_v2/models/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/desktop/src/components/settings_v2/models/index.ts b/ui/desktop/src/components/settings_v2/models/index.ts index 950a62056632..1fd4713f48e4 100644 --- a/ui/desktop/src/components/settings_v2/models/index.ts +++ b/ui/desktop/src/components/settings_v2/models/index.ts @@ -112,8 +112,8 @@ interface getCurrentModelAndProviderProps { } export async function getCurrentModelAndProvider({ - readFromConfig, -}: getCurrentModelAndProviderProps) { + readFromConfig, + }: getCurrentModelAndProviderProps) { let model: string; let provider: string; @@ -135,9 +135,9 @@ interface getCurrentModelAndProviderForDisplayProps { // returns display name of the provider export async function getCurrentModelAndProviderForDisplay({ - readFromConfig, - getProviders, -}: getCurrentModelAndProviderForDisplayProps) { + readFromConfig, + getProviders, + }: getCurrentModelAndProviderForDisplayProps) { const modelProvider = await getCurrentModelAndProvider({ readFromConfig: readFromConfig }); const gooseModel = modelProvider.model; const gooseProvider = modelProvider.provider; @@ -157,4 +157,4 @@ export async function getCurrentModelAndProviderForDisplay({ const providerDisplayName = providerDetailsList[0].metadata.display_name; return { model: gooseModel, provider: providerDisplayName }; -} +} \ No newline at end of file From 1a9e78a7db84bef585aac554e38e7f71c99850a0 Mon Sep 17 00:00:00 2001 From: Lily Delalande Date: Tue, 25 Mar 2025 21:13:35 -0400 Subject: [PATCH 2/7] rebase --- .../components/settings_v2/models/index.ts | 12 ++++----- .../subcomponents/ConfigureModelButtons.tsx | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 ui/desktop/src/components/settings_v2/models/subcomponents/ConfigureModelButtons.tsx diff --git a/ui/desktop/src/components/settings_v2/models/index.ts b/ui/desktop/src/components/settings_v2/models/index.ts index 1fd4713f48e4..950a62056632 100644 --- a/ui/desktop/src/components/settings_v2/models/index.ts +++ b/ui/desktop/src/components/settings_v2/models/index.ts @@ -112,8 +112,8 @@ interface getCurrentModelAndProviderProps { } export async function getCurrentModelAndProvider({ - readFromConfig, - }: getCurrentModelAndProviderProps) { + readFromConfig, +}: getCurrentModelAndProviderProps) { let model: string; let provider: string; @@ -135,9 +135,9 @@ interface getCurrentModelAndProviderForDisplayProps { // returns display name of the provider export async function getCurrentModelAndProviderForDisplay({ - readFromConfig, - getProviders, - }: getCurrentModelAndProviderForDisplayProps) { + readFromConfig, + getProviders, +}: getCurrentModelAndProviderForDisplayProps) { const modelProvider = await getCurrentModelAndProvider({ readFromConfig: readFromConfig }); const gooseModel = modelProvider.model; const gooseProvider = modelProvider.provider; @@ -157,4 +157,4 @@ export async function getCurrentModelAndProviderForDisplay({ const providerDisplayName = providerDetailsList[0].metadata.display_name; return { model: gooseModel, provider: providerDisplayName }; -} \ No newline at end of file +} diff --git a/ui/desktop/src/components/settings_v2/models/subcomponents/ConfigureModelButtons.tsx b/ui/desktop/src/components/settings_v2/models/subcomponents/ConfigureModelButtons.tsx new file mode 100644 index 000000000000..179acb0e76ab --- /dev/null +++ b/ui/desktop/src/components/settings_v2/models/subcomponents/ConfigureModelButtons.tsx @@ -0,0 +1,26 @@ +import { AddModelButton } from './AddModelButton'; +import { Button } from '../../../ui/button'; +import { Sliders } from 'lucide-react'; +import React from 'react'; +import type { View } from '../../../../App'; + +interface ConfigureModelButtonsProps { + setView: (view: View) => void; +} + +export default function ConfigureModelButtons({ setView }: ConfigureModelButtonsProps) { + return ( +
+ + +
+ ); +} From f86b5fc05c07225631896b0e930133a46e0321c5 Mon Sep 17 00:00:00 2001 From: Lily Delalande Date: Tue, 25 Mar 2025 21:15:06 -0400 Subject: [PATCH 3/7] rebase --- .../models/subcomponents/BottomBar.tsx | 69 +++++++++++++++++++ .../subcomponents/ConfigureModelButtons.tsx | 26 ------- .../subcomponents/CurrentModelProvider.tsx | 27 ++++++++ 3 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 ui/desktop/src/components/settings_v2/models/subcomponents/BottomBar.tsx delete mode 100644 ui/desktop/src/components/settings_v2/models/subcomponents/ConfigureModelButtons.tsx create mode 100644 ui/desktop/src/components/settings_v2/models/subcomponents/CurrentModelProvider.tsx diff --git a/ui/desktop/src/components/settings_v2/models/subcomponents/BottomBar.tsx b/ui/desktop/src/components/settings_v2/models/subcomponents/BottomBar.tsx new file mode 100644 index 000000000000..6c48f5dd5ee7 --- /dev/null +++ b/ui/desktop/src/components/settings_v2/models/subcomponents/BottomBar.tsx @@ -0,0 +1,69 @@ +import { ChevronDown, ChevronUp } from '../../../icons'; +import { Sliders } from 'lucide-react'; +import React, { useEffect, useState } from 'react'; +import { useConfig } from '../../../ConfigContext'; +import { getCurrentModelAndProviderForDisplay } from '../index'; +import { AddModelModal } from './AddModelModal'; + +interface ModelsBottomBarProps { + dropdownRef: any; + setView: any; +} +export default function ModelsBottomBar({ dropdownRef }: ModelsBottomBarProps) { + const { read, getProviders } = useConfig(); + const [isModelMenuOpen, setIsModelMenuOpen] = useState(false); + const [provider, setProvider] = useState(null); + const [model, setModel] = useState(''); + const [isAddModelModalOpen, setIsAddModelModalOpen] = useState(false); + + useEffect(() => { + (async () => { + const modelProvider = await getCurrentModelAndProviderForDisplay({ + readFromConfig: read, + getProviders, + }); + setProvider(modelProvider.provider); + setModel(modelProvider.model); + })(); + }, [read, getProviders]); + + return ( +
+
setIsModelMenuOpen(!isModelMenuOpen)} + > + {model} + {isModelMenuOpen ? ( + + ) : ( + + )} +
+ + {/* Dropdown Menu */} + {isModelMenuOpen && ( +
+
+
Current:
+
+ {model} -- {provider} +
+
{ + setIsModelMenuOpen(false); + setIsAddModelModalOpen(true); + }} + > + Change Model + +
+
+
+ )} + {isAddModelModalOpen ? setIsAddModelModalOpen(false)} /> : null} +
+ ); +} diff --git a/ui/desktop/src/components/settings_v2/models/subcomponents/ConfigureModelButtons.tsx b/ui/desktop/src/components/settings_v2/models/subcomponents/ConfigureModelButtons.tsx deleted file mode 100644 index 179acb0e76ab..000000000000 --- a/ui/desktop/src/components/settings_v2/models/subcomponents/ConfigureModelButtons.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { AddModelButton } from './AddModelButton'; -import { Button } from '../../../ui/button'; -import { Sliders } from 'lucide-react'; -import React from 'react'; -import type { View } from '../../../../App'; - -interface ConfigureModelButtonsProps { - setView: (view: View) => void; -} - -export default function ConfigureModelButtons({ setView }: ConfigureModelButtonsProps) { - return ( -
- - -
- ); -} diff --git a/ui/desktop/src/components/settings_v2/models/subcomponents/CurrentModelProvider.tsx b/ui/desktop/src/components/settings_v2/models/subcomponents/CurrentModelProvider.tsx new file mode 100644 index 000000000000..24d61cab7a2d --- /dev/null +++ b/ui/desktop/src/components/settings_v2/models/subcomponents/CurrentModelProvider.tsx @@ -0,0 +1,27 @@ +import React, { useEffect, useState } from 'react'; +import { useConfig } from '../../../ConfigContext'; +import { getCurrentModelAndProviderForDisplay } from '@/src/components/settings_v2/models'; + +export default function CurrentModelProvider(arg) { + const [provider, setProvider] = useState(null); + const [model, setModel] = useState(''); + const { read, getProviders } = useConfig(); + + useEffect(() => { + (async () => { + const modelProvider = await getCurrentModelAndProviderForDisplay({ + readFromConfig: read, + getProviders, + }); + setProvider(modelProvider.provider); + setModel(modelProvider.model); + })(); + }, [read, getProviders]); + + return ( +
+

{model}

+

{provider}

+
+ ); +} From c1c429b756daa58e3925c5facd346e3ed9402ecd Mon Sep 17 00:00:00 2001 From: Lily Delalande Date: Tue, 25 Mar 2025 21:15:32 -0400 Subject: [PATCH 4/7] rebase --- .../models/subcomponents/BottomBar.tsx | 69 ------------------- 1 file changed, 69 deletions(-) delete mode 100644 ui/desktop/src/components/settings_v2/models/subcomponents/BottomBar.tsx diff --git a/ui/desktop/src/components/settings_v2/models/subcomponents/BottomBar.tsx b/ui/desktop/src/components/settings_v2/models/subcomponents/BottomBar.tsx deleted file mode 100644 index 6c48f5dd5ee7..000000000000 --- a/ui/desktop/src/components/settings_v2/models/subcomponents/BottomBar.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { ChevronDown, ChevronUp } from '../../../icons'; -import { Sliders } from 'lucide-react'; -import React, { useEffect, useState } from 'react'; -import { useConfig } from '../../../ConfigContext'; -import { getCurrentModelAndProviderForDisplay } from '../index'; -import { AddModelModal } from './AddModelModal'; - -interface ModelsBottomBarProps { - dropdownRef: any; - setView: any; -} -export default function ModelsBottomBar({ dropdownRef }: ModelsBottomBarProps) { - const { read, getProviders } = useConfig(); - const [isModelMenuOpen, setIsModelMenuOpen] = useState(false); - const [provider, setProvider] = useState(null); - const [model, setModel] = useState(''); - const [isAddModelModalOpen, setIsAddModelModalOpen] = useState(false); - - useEffect(() => { - (async () => { - const modelProvider = await getCurrentModelAndProviderForDisplay({ - readFromConfig: read, - getProviders, - }); - setProvider(modelProvider.provider); - setModel(modelProvider.model); - })(); - }, [read, getProviders]); - - return ( -
-
setIsModelMenuOpen(!isModelMenuOpen)} - > - {model} - {isModelMenuOpen ? ( - - ) : ( - - )} -
- - {/* Dropdown Menu */} - {isModelMenuOpen && ( -
-
-
Current:
-
- {model} -- {provider} -
-
{ - setIsModelMenuOpen(false); - setIsAddModelModalOpen(true); - }} - > - Change Model - -
-
-
- )} - {isAddModelModalOpen ? setIsAddModelModalOpen(false)} /> : null} -
- ); -} From 4b04fd06ceca28c8bfae96a2dbbd2a94e86aa88a Mon Sep 17 00:00:00 2001 From: Lily Delalande Date: Tue, 25 Mar 2025 19:24:04 -0400 Subject: [PATCH 5/7] initial attempt --- ui/desktop/src/App.tsx | 31 ++++++++++++++----- .../settings_v2/extensions/index.ts | 22 ++++++++++++- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index 1aa92eba91ab..ef62e51746dc 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -24,12 +24,14 @@ import ProviderSettings from './components/settings_v2/providers/ProviderSetting import { useChat } from './hooks/useChat'; import 'react-toastify/dist/ReactToastify.css'; -import { useConfig } from './components/ConfigContext'; +import { FixedExtensionEntry, useConfig } from './components/ConfigContext'; import { initializeBuiltInExtensions, syncBuiltInExtensions, addExtensionFromDeepLink as addExtensionFromDeepLinkV2, + addToAgentOnStartup, } from './components/settings_v2/extensions'; +import { extractExtensionConfig } from './components/settings_v2/extensions/utils'; // Views and their options export type View = @@ -83,18 +85,23 @@ export default function App() { console.log('Alpha flow initializing...'); const setupExtensions = async () => { + let refreshedExtensions: FixedExtensionEntry[] = []; try { - console.log('Setting up extensions...'); + // Force refresh extensions from the backend to ensure we have the latest + console.log('Getting extensions from backend...'); + refreshedExtensions = await getExtensions(true); + console.log(`Retrieved ${refreshedExtensions.length} extensions`); + } catch (error) { + console.log('Error getting extensions list'); + } + // built-in extensions block -- just adds them to config if missing + try { + console.log('Setting up built-in extensions...'); // Set the ref immediately to prevent duplicate runs initAttemptedRef.current = true; console.log('Set initAttemptedRef to prevent duplicate runs'); - // Force refresh extensions from the backend to ensure we have the latest - console.log('Getting extensions from backend...'); - const refreshedExtensions = await getExtensions(true); - console.log(`Retrieved ${refreshedExtensions.length} extensions`); - if (refreshedExtensions.length === 0) { // If we still have no extensions, this is truly a first-time setup console.log('First-time setup: Adding all built-in extensions...'); @@ -116,6 +123,16 @@ export default function App() { }); // We don't set fatal error here since the app might still work without extensions } + + // now try to add to agent + for (const extensionEntry of refreshedExtensions) { + if (extensionEntry.enabled) { + // need to convert to config because that's what the endpoint expects + const extensionConfig = extractExtensionConfig(extensionEntry); + // will handle toasts and also set failures to enabled = false + await addToAgentOnStartup({ addToConfig: addExtension, extensionConfig }); + } + } }; const initializeApp = async () => { diff --git a/ui/desktop/src/components/settings_v2/extensions/index.ts b/ui/desktop/src/components/settings_v2/extensions/index.ts index ff33ffa6e270..b2bb4209c9bf 100644 --- a/ui/desktop/src/components/settings_v2/extensions/index.ts +++ b/ui/desktop/src/components/settings_v2/extensions/index.ts @@ -96,6 +96,27 @@ export async function activateExtension({ } } +interface addToAgentOnStartupProps { + addToConfig: (name: string, extensionConfig: ExtensionConfig, enabled: boolean) => Promise; + extensionConfig: ExtensionConfig; +} + +export async function addToAgentOnStartup({ + addToConfig, + extensionConfig, +}: addToAgentOnStartupProps): Promise { + try { + // AddToAgent + await AddToAgent(extensionConfig); + } catch (error) { + // update config with enabled = false + await toggleExtension({ toggle: 'toggleOff', extensionConfig, addToConfig }); + // show user the error, return + console.log('error', error); + return; + } +} + interface updateExtensionProps { enabled: boolean; addToConfig: (name: string, extensionConfig: ExtensionConfig, enabled: boolean) => Promise; @@ -141,7 +162,6 @@ interface toggleExtensionProps { toggle: 'toggleOn' | 'toggleOff'; extensionConfig: ExtensionConfig; addToConfig: (name: string, extensionConfig: ExtensionConfig, enabled: boolean) => Promise; - removeFromConfig: (name: string) => Promise; } export async function toggleExtension({ From 7f4e6b5ece9454f6a0a9f1ee5abe38951d503644 Mon Sep 17 00:00:00 2001 From: Lily Delalande Date: Tue, 25 Mar 2025 20:34:52 -0400 Subject: [PATCH 6/7] bubble up errors --- ui/desktop/src/App.tsx | 121 +++++++------ .../components/settings_v2/SettingsView.tsx | 4 - .../settings_v2/extensions/index.ts | 160 ++++++++++++------ 3 files changed, 171 insertions(+), 114 deletions(-) diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index ef62e51746dc..f5a0318647f0 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -76,6 +76,8 @@ export default function App() { } // this is all settings v2 stuff + // Modified version of the alpha initialization flow for App.tsx + useEffect(() => { // Skip if feature flag is not enabled if (!process.env.ALPHA) { @@ -84,7 +86,45 @@ export default function App() { console.log('Alpha flow initializing...'); + // First quickly check if we have model and provider to set chat view + const checkRequiredConfig = async () => { + try { + console.log('Reading GOOSE_PROVIDER and GOOSE_MODEL from config...'); + const provider = (await read('GOOSE_PROVIDER', false)) as string; + const model = (await read('GOOSE_MODEL', false)) as string; + + if (provider && model) { + // We have all needed configuration, set chat view immediately + console.log(`Found provider: ${provider}, model: ${model}, setting chat view`); + setView('chat'); + + // Initialize the system in background + initializeSystem(provider, model) + .then(() => console.log('System initialization successful')) + .catch((error) => { + console.error('Error initializing system:', error); + setFatalError(`System initialization error: ${error.message || 'Unknown error'}`); + setView('welcome'); + }); + } else { + // Missing configuration, show onboarding + console.log('Missing configuration, showing onboarding'); + if (!provider) console.log('Missing provider'); + if (!model) console.log('Missing model'); + setView('welcome'); + } + } catch (error) { + console.error('Error checking configuration:', error); + setFatalError(`Configuration check error: ${error.message || 'Unknown error'}`); + setView('welcome'); + } + }; + + // Setup extensions in parallel const setupExtensions = async () => { + // Set the ref immediately to prevent duplicate runs + initAttemptedRef.current = true; + let refreshedExtensions: FixedExtensionEntry[] = []; try { // Force refresh extensions from the backend to ensure we have the latest @@ -93,20 +133,21 @@ export default function App() { console.log(`Retrieved ${refreshedExtensions.length} extensions`); } catch (error) { console.log('Error getting extensions list'); + return; // Exit early if we can't get the extensions list } + // built-in extensions block -- just adds them to config if missing try { console.log('Setting up built-in extensions...'); - // Set the ref immediately to prevent duplicate runs - initAttemptedRef.current = true; - console.log('Set initAttemptedRef to prevent duplicate runs'); - if (refreshedExtensions.length === 0) { // If we still have no extensions, this is truly a first-time setup console.log('First-time setup: Adding all built-in extensions...'); await initializeBuiltInExtensions(addExtension); console.log('Built-in extensions initialization complete'); + + // Refresh the extensions list after initialization + refreshedExtensions = await getExtensions(true); } else { // Extensions exist, check for any missing built-ins console.log('Checking for missing built-in extensions...'); @@ -116,79 +157,37 @@ export default function App() { } } catch (error) { console.error('Error setting up extensions:', error); - console.error('Extension setup error details:', { - message: error.message, - stack: error.stack, - name: error.name, - }); // We don't set fatal error here since the app might still work without extensions } // now try to add to agent + console.log('Adding enabled extensions to agent...'); for (const extensionEntry of refreshedExtensions) { if (extensionEntry.enabled) { + console.log(`Adding extension to agent: ${extensionEntry.name}`); // need to convert to config because that's what the endpoint expects const extensionConfig = extractExtensionConfig(extensionEntry); // will handle toasts and also set failures to enabled = false await addToAgentOnStartup({ addToConfig: addExtension, extensionConfig }); - } - } - }; - - const initializeApp = async () => { - try { - console.log('Initializing alpha app...'); - - // Check if we have the required configuration - console.log('Reading GOOSE_PROVIDER from config...'); - const provider = (await read('GOOSE_PROVIDER', false)) as string; - console.log('Provider from config:', provider); - - console.log('Reading GOOSE_MODEL from config...'); - const model = (await read('GOOSE_MODEL', false)) as string; - console.log('Model from config:', model); - - if (provider && model) { - // We have all needed configuration, initialize the system - console.log(`Initializing system with provider: ${provider}, model: ${model}`); - await initializeSystem(provider, model); - console.log('System initialization successful'); - setView('chat'); } else { - // Missing configuration, show onboarding - console.log('Missing configuration, showing onboarding'); - if (!provider) console.log('Missing provider'); - if (!model) console.log('Missing model'); - setView('welcome'); + console.log(`Skipping disabled extension: ${extensionEntry.name}`); } - } catch (error) { - console.error('Error initializing app:', error); - console.error('App initialization error details:', { - message: error.message, - stack: error.stack, - name: error.name, - }); - setFatalError(`Alpha initialization error: ${error.message || 'Unknown error'}`); - setView('welcome'); } + + console.log('Extensions setup complete'); }; - // Execute with better promise handling - initializeApp() - .then(() => console.log('Alpha app initialization complete')) - .catch((error) => { - console.error('Unhandled error in initializeApp:', error); - setFatalError(`Unhandled alpha app error: ${error.message || 'Unknown error'}`); - }); - - setupExtensions() - .then(() => console.log('Extensions setup complete')) - .catch((error) => { - console.error('Unhandled error in setupExtensions:', error); - // Not setting fatal error here since extensions are optional - }); - }, []); // Empty dependency array since we're using initAttemptedRef + // Execute the two flows in parallel for speed + checkRequiredConfig().catch((error) => { + console.error('Unhandled error in checkRequiredConfig:', error); + setFatalError(`Config check error: ${error.message || 'Unknown error'}`); + }); + setupExtensions().catch((error) => { + console.error('Unhandled error in setupExtensions:', error); + // Not setting fatal error here since extensions are optional + }); + }, []); // Empty dependency array since we're using initAttemptedRef const setView = (view: View, viewOptions: Record = {}) => { console.log(`Setting view to: ${view}`, viewOptions); setInternalView({ view, viewOptions }); diff --git a/ui/desktop/src/components/settings_v2/SettingsView.tsx b/ui/desktop/src/components/settings_v2/SettingsView.tsx index c0362712d4ab..32f33b908a3d 100644 --- a/ui/desktop/src/components/settings_v2/SettingsView.tsx +++ b/ui/desktop/src/components/settings_v2/SettingsView.tsx @@ -20,10 +20,6 @@ export default function SettingsView({ setView: (view: View) => void; viewOptions: SettingsViewOptions; }) { - const { config } = useConfig(); - - console.log(config); - return (
diff --git a/ui/desktop/src/components/settings_v2/extensions/index.ts b/ui/desktop/src/components/settings_v2/extensions/index.ts index b2bb4209c9bf..7da160f248ee 100644 --- a/ui/desktop/src/components/settings_v2/extensions/index.ts +++ b/ui/desktop/src/components/settings_v2/extensions/index.ts @@ -78,21 +78,26 @@ export async function activateExtension({ // AddToAgent await AddToAgent(extensionConfig); } catch (error) { + console.error('Failed to add extension to agent:', error); // add to config with enabled = false await addToConfig(extensionConfig.name, extensionConfig, false); - // show user the error, return - console.log('error', error); - return; + // Rethrow the error to inform the caller + throw error; } // Then add to config try { await addToConfig(extensionConfig.name, extensionConfig, true); } catch (error) { + console.error('Failed to add extension to config:', error); // remove from Agent - await RemoveFromAgent(extensionConfig.name); - // config error workflow - console.log('error', error); + try { + await RemoveFromAgent(extensionConfig.name); + } catch (removeError) { + console.error('Failed to remove extension from agent after config failure:', removeError); + } + // Rethrow the error to inform the caller + throw error; } } @@ -109,11 +114,15 @@ export async function addToAgentOnStartup({ // AddToAgent await AddToAgent(extensionConfig); } catch (error) { + console.log('got error trying to add to agent in addAgentOnStartUp', error); // update config with enabled = false - await toggleExtension({ toggle: 'toggleOff', extensionConfig, addToConfig }); - // show user the error, return - console.log('error', error); - return; + try { + await toggleExtension({ toggle: 'toggleOff', extensionConfig, addToConfig }); + } catch (toggleError) { + console.error('Failed to toggle extension off after agent error:', toggleError); + } + // Rethrow the error to inform the caller + throw error; } } @@ -134,26 +143,24 @@ export async function updateExtension({ // AddToAgent await AddToAgent(extensionConfig); } catch (error) { - // i think only error that gets thrown here is when it's not from the response... rest are handled by agent - console.log('error', error); - // failed to add to agent -- show that error to user and do not update the config file - return; + console.error('Failed to add extension to agent during update:', error); + // Failed to add to agent -- show that error to user and do not update the config file + throw error; } // Then add to config try { await addToConfig(extensionConfig.name, extensionConfig, enabled); } catch (error) { - // config error workflow - console.log('error', error); + console.error('Failed to update extension in config:', error); + throw error; } } else { try { await addToConfig(extensionConfig.name, extensionConfig, enabled); } catch (error) { - // TODO: Add to agent with previous configuration and raise error - // for now just log error - console.log('error', error); + console.error('Failed to update disabled extension in config:', error); + throw error; } } } @@ -175,33 +182,50 @@ export async function toggleExtension({ // add to agent await AddToAgent(extensionConfig); } catch (error) { - // do nothing raise error - // show user error - console.log('Error adding extension to agent. Error:', error); - return; + console.error('Error adding extension to agent. Will try to toggle back off. Error:', error); + try { + await toggleExtension({ toggle: 'toggleOff', extensionConfig, addToConfig }); + } catch (toggleError) { + console.error('Failed to toggle extension off after agent error:', toggleError); + } + throw error; } // update the config try { await addToConfig(extensionConfig.name, extensionConfig, true); } catch (error) { - // remove from agent? - await RemoveFromAgent(extensionConfig.name); + console.error('Failed to update config after enabling extension:', error); + // remove from agent + try { + await RemoveFromAgent(extensionConfig.name); + } catch (removeError) { + console.error('Failed to remove extension from agent after config failure:', removeError); + } + throw error; } } else if (toggle == 'toggleOff') { // enabled to disabled + let agentRemoveError = null; try { await RemoveFromAgent(extensionConfig.name); } catch (error) { - // note there was an error, but remove from config anyway + // note there was an error, but attempt to remove from config anyway console.error('Error removing extension from agent', extensionConfig.name, error); + agentRemoveError = error; } + // update the config try { await addToConfig(extensionConfig.name, extensionConfig, false); } catch (error) { - // TODO: Add to agent with previous configuration - console.log('Error removing extension from config', extensionConfig.name, 'Error:', error); + console.error('Error removing extension from config', extensionConfig.name, 'Error:', error); + throw error; + } + + // If we had an error removing from agent but succeeded updating config, still throw the original error + if (agentRemoveError) { + throw agentRemoveError; } } } @@ -213,15 +237,29 @@ interface deleteExtensionProps { export async function deleteExtension({ name, removeFromConfig }: deleteExtensionProps) { // remove from agent - await RemoveFromAgent(name); + let agentRemoveError = null; + try { + await RemoveFromAgent(name); + } catch (error) { + console.error('Failed to remove extension from agent during deletion:', error); + agentRemoveError = error; + } try { await removeFromConfig(name); } catch (error) { - console.log('Failed to remove extension from config after removing from agent. Error:', error); - // TODO: tell user to restart goose and try again to remove (will still be present in settings but not on agent until restart) + console.error( + 'Failed to remove extension from config after removing from agent. Error:', + error + ); + // If we also had an agent remove error, log it but throw the config error as it's more critical throw error; } + + // If we had an error removing from agent but succeeded removing from config, still throw the original error + if (agentRemoveError) { + throw agentRemoveError; + } } { @@ -312,7 +350,12 @@ export async function addExtensionFromDeepLink( } // If no env vars are required, proceed with adding the extension - await activateExtension({ extensionConfig: config, addToConfig: addExtensionFn }); + try { + await activateExtension({ extensionConfig: config, addToConfig: addExtensionFn }); + } catch (error) { + console.error('Failed to activate extension from deeplink:', error); + throw error; + } } { @@ -360,8 +403,13 @@ export async function syncBuiltInExtensions( }; // Add the extension with its default enabled state - await addExtensionFn(nameToKey(builtinExt.name), extConfig, builtinExt.enabled); - addedCount++; + try { + await addExtensionFn(nameToKey(builtinExt.name), extConfig, builtinExt.enabled); + addedCount++; + } catch (error) { + console.error(`Failed to add built-in extension ${builtinExt.name}:`, error); + // Continue with other extensions even if one fails + } } } @@ -435,7 +483,7 @@ async function extensionApiCall( msg: 'Agent is not initialized. Please initialize the agent first.', traceback: errorMsg, }); - return response; + throw new Error('Agent is not initialized. Please initialize the agent first.'); } const msg = `Failed to ${actionType === 'adding' ? 'add' : 'remove'} ${extensionName} extension: ${errorMsg}`; @@ -447,7 +495,7 @@ async function extensionApiCall( msg: msg, traceback: errorMsg, }); - return response; + throw new Error(msg); } // Parse response JSON safely @@ -455,40 +503,54 @@ async function extensionApiCall( try { const text = await response.text(); data = text ? JSON.parse(text) : { error: false }; - } catch (error) { - console.warn('Could not parse response as JSON, assuming success', error); + } catch (parseError) { + console.warn('Could not parse response as JSON, assuming success', parseError); data = { error: false }; } if (!data.error) { if (toastId) toast.dismiss(toastId); - ToastSuccess({ title: extensionName, msg: 'Successfully enabled extension' }); + ToastSuccess({ title: extensionName, msg: `Successfully ${pastVerb} extension` }); + return response; } else { - const errorMessage = `Error adding extension -- parsing data`; + const errorMessage = `Error ${actionType} extension -- parsing data: ${data.message || 'Unknown error'}`; console.error(errorMessage); if (toastId) toast.dismiss(toastId); ToastError({ title: extensionName, msg: errorMessage, - traceback: data.message, // why data.message not data.error? + traceback: data.message || 'Unknown error', // why data.message not data.error? }); + throw new Error(errorMessage); } } catch (error) { - // + if (toastId) toast.dismiss(toastId); + console.error(`Error in extensionApiCall for ${extensionName}:`, error); + throw error; } } // Public functions export async function AddToAgent(extension: ExtensionConfig): Promise { - if (extension.type === 'stdio') { - console.log('extension command', extension.cmd); - extension.cmd = await replaceWithShims(extension.cmd); - console.log('next ext command', extension.cmd); - } + try { + if (extension.type === 'stdio') { + console.log('extension command', extension.cmd); + extension.cmd = await replaceWithShims(extension.cmd); + console.log('next ext command', extension.cmd); + } - return extensionApiCall('/extensions/add', extension, 'adding', extension.name); + return await extensionApiCall('/extensions/add', extension, 'adding', extension.name); + } catch (error) { + console.error(`Failed to add extension ${extension.name} to agent:`, error); + throw error; + } } export async function RemoveFromAgent(name: string): Promise { - return extensionApiCall('/extensions/remove', name, 'removing', name); + try { + return await extensionApiCall('/extensions/remove', name, 'removing', name); + } catch (error) { + console.error(`Failed to remove extension ${name} from agent:`, error); + throw error; + } } From 7d5990e9dd3b72ebed2e9f0907f03eab2de82d13 Mon Sep 17 00:00:00 2001 From: Lily Delalande Date: Tue, 25 Mar 2025 20:56:00 -0400 Subject: [PATCH 7/7] remove unused file --- .../subcomponents/CurrentModelProvider.tsx | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 ui/desktop/src/components/settings_v2/models/subcomponents/CurrentModelProvider.tsx diff --git a/ui/desktop/src/components/settings_v2/models/subcomponents/CurrentModelProvider.tsx b/ui/desktop/src/components/settings_v2/models/subcomponents/CurrentModelProvider.tsx deleted file mode 100644 index 24d61cab7a2d..000000000000 --- a/ui/desktop/src/components/settings_v2/models/subcomponents/CurrentModelProvider.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useConfig } from '../../../ConfigContext'; -import { getCurrentModelAndProviderForDisplay } from '@/src/components/settings_v2/models'; - -export default function CurrentModelProvider(arg) { - const [provider, setProvider] = useState(null); - const [model, setModel] = useState(''); - const { read, getProviders } = useConfig(); - - useEffect(() => { - (async () => { - const modelProvider = await getCurrentModelAndProviderForDisplay({ - readFromConfig: read, - getProviders, - }); - setProvider(modelProvider.provider); - setModel(modelProvider.model); - })(); - }, [read, getProviders]); - - return ( -
-

{model}

-

{provider}

-
- ); -}