From 9c290c569f35f61241aba48bd573ba714363092e Mon Sep 17 00:00:00 2001 From: Alex Hancock Date: Thu, 27 Mar 2025 15:40:27 -0400 Subject: [PATCH] chore: Initial pass at cleanup of initializeAgent (#1888) --- ui/desktop/openapi.json | 2 +- ui/desktop/src/agent/UpdateAgent.tsx | 188 ------------------ ui/desktop/src/agent/utils.tsx | 16 -- .../settings_v2/extensions/agent-api.ts | 17 +- .../settings_v2/extensions/utils.ts | 16 ++ .../providers/ProviderSettingsPage.tsx | 5 +- ui/desktop/src/utils/providerUtils.ts | 22 +- 7 files changed, 23 insertions(+), 243 deletions(-) delete mode 100644 ui/desktop/src/agent/UpdateAgent.tsx diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 867e7e56..940346bd 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -10,7 +10,7 @@ "license": { "name": "Apache-2.0" }, - "version": "1.0.15" + "version": "1.0.16" }, "paths": { "/config": { diff --git a/ui/desktop/src/agent/UpdateAgent.tsx b/ui/desktop/src/agent/UpdateAgent.tsx deleted file mode 100644 index a049ff83..00000000 --- a/ui/desktop/src/agent/UpdateAgent.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { useConfig, FixedExtensionEntry } from '../components/ConfigContext'; -import { getApiUrl, getSecretKey } from '../config'; -import { ExtensionConfig } from '../api'; -import { toast } from 'react-toastify'; -import React, { useState } from 'react'; -import { initializeAgent as startAgent, replaceWithShims } from './utils'; -import { toastError, toastInfo, toastLoading, toastSuccess } from '../toasts'; - -// extensionUpdate = an extension was newly added or updated so we should attempt to add it - -export const useAgent = () => { - const { getExtensions, read } = useConfig(); - const [isUpdating, setIsUpdating] = useState(false); - - // whenever we change the model, we must call this - const initializeAgent = async (provider: string, model: string) => { - try { - console.log('Initializing agent with provider', provider, 'model', model); - - const response = await startAgent(model, provider); - - if (!response.ok) { - throw new Error(`Failed to initialize agent: ${response.statusText}`); - } - - return true; - } catch (error) { - console.error('Failed to initialize agent:', error); - toastError({ - title: 'Failed to initialize agent', - traceback: error instanceof Error ? error.message : 'Unknown error', - }); - return false; - } - }; - - const updateAgent = async (extensionUpdate?: ExtensionConfig) => { - setIsUpdating(true); - - try { - // need to initialize agent first (i dont get why but if we dont do this, we get a 428) - // note: we must write the value for GOOSE_MODEL and GOOSE_PROVIDER in the config before updating agent - const goose_model = (await read('GOOSE_MODEL', false)) as string; - const goose_provider = (await read('GOOSE_PROVIDER', false)) as string; - - console.log( - `Starting agent with GOOSE_MODEL=${goose_model} and GOOSE_PROVIDER=${goose_provider}` - ); - - // Initialize the agent if it's a model change - if (goose_model && goose_provider) { - const success = await initializeAgent(goose_provider, goose_model); - if (!success) { - console.error('Failed to initialize agent during model change'); - return false; - } - } - - if (extensionUpdate) { - await addExtensionToAgent(extensionUpdate); - } - - return true; - } catch (error) { - console.error('Error updating agent:', error); - return false; - } finally { - setIsUpdating(false); - } - }; - - // TODO: set 'enabled' to false if we fail to start / add the extension - // only for non-builtins - - // TODO: try to add some descriptive error messages for common failure modes - const addExtensionToAgent = async ( - extension: ExtensionConfig, - silent: boolean = false - ): 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 { - let toastId; - if (!silent) { - toastId = toastLoading({ - title: extension.name, - msg: 'Adding extension...', - toastOptions: { position: 'top-center' }, - }); - toastInfo({ - msg: 'Press the escape key to continue using goose while extension loads', - }); - } - - const response = await fetch(getApiUrl('/extensions/add'), { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Secret-Key': getSecretKey(), - }, - body: JSON.stringify(extension), - }); - - // Handle non-OK responses - if (!response.ok) { - const errorMsg = `Server returned ${response.status}: ${response.statusText}`; - console.error(errorMsg); - - // Special handling for 428 Precondition Required (agent not initialized) - if (response.status === 428) { - if (!silent) { - if (toastId) toast.dismiss(toastId); - toastError({ - msg: 'Agent is not initialized. Please initialize the agent first.', - }); - } - return response; - } - - if (!silent) { - if (toastId) toast.dismiss(toastId); - toastError({ - title: extension.name, - msg: 'Failed to add extension', - traceback: errorMsg, - }); - } - return response; - } - - // Parse response JSON safely - let data; - 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); - data = { error: false }; - } - - console.log('Response data:', data); - - if (!data.error) { - if (!silent) { - if (toastId) toast.dismiss(toastId); - toastSuccess({ - title: extension.name, - msg: 'Successfully added extension', - }); - } - return response; - } - - console.log('Error trying to send a request to the extensions endpoint'); - const errorMessage = `Error adding ${extension.name} extension${data.message ? `. ${data.message}` : ''}`; - console.error(errorMessage); - if (toastId) toast.dismiss(toastId); - toastError({ - title: extension.name, - msg: 'Failed to add extension', - traceback: data.message, - }); - - return response; - } catch (error) { - console.log('Got some other error'); - const errorMessage = `Failed to add ${extension.name} extension: ${error instanceof Error ? error.message : 'Unknown error'}`; - console.error(errorMessage); - toastError({ - title: extension.name, - msg: 'Failed to add extension', - traceback: error.message, - }); - throw error; - } - }; - - return { - updateAgent, - addExtensionToAgent, - initializeAgent, - isUpdating, - }; -}; diff --git a/ui/desktop/src/agent/utils.tsx b/ui/desktop/src/agent/utils.tsx index 17f36a4f..26dfd64a 100644 --- a/ui/desktop/src/agent/utils.tsx +++ b/ui/desktop/src/agent/utils.tsx @@ -15,19 +15,3 @@ export async function initializeAgent(model: string, provider: string) { }); return response; } - -export async function replaceWithShims(cmd: string) { - const binaryPathMap: Record = { - goosed: await window.electron.getBinaryPath('goosed'), - jbang: await window.electron.getBinaryPath('jbang'), - npx: await window.electron.getBinaryPath('npx'), - uvx: await window.electron.getBinaryPath('uvx'), - }; - - if (binaryPathMap[cmd]) { - console.log('--------> Replacing command with shim ------>', cmd, binaryPathMap[cmd]); - cmd = binaryPathMap[cmd]; - } - - return cmd; -} diff --git a/ui/desktop/src/components/settings_v2/extensions/agent-api.ts b/ui/desktop/src/components/settings_v2/extensions/agent-api.ts index 4f272220..17a08524 100644 --- a/ui/desktop/src/components/settings_v2/extensions/agent-api.ts +++ b/ui/desktop/src/components/settings_v2/extensions/agent-api.ts @@ -1,6 +1,7 @@ import { ExtensionConfig } from '../../../api/types.gen'; import { getApiUrl, getSecretKey } from '../../../config'; import { toastService, ToastServiceOptions } from '../../../toasts'; +import { replaceWithShims } from './utils'; /** * Makes an API call to the extension endpoints @@ -166,19 +167,3 @@ export async function removeFromAgent( throw error; } } - -// Update the path to the binary based on the command -async function replaceWithShims(cmd: string): Promise { - const binaryPathMap: Record = { - goosed: await window.electron.getBinaryPath('goosed'), - npx: await window.electron.getBinaryPath('npx'), - uvx: await window.electron.getBinaryPath('uvx'), - }; - - if (binaryPathMap[cmd]) { - console.log('--------> Replacing command with shim ------>', cmd, binaryPathMap[cmd]); - return binaryPathMap[cmd]; - } - - return cmd; -} diff --git a/ui/desktop/src/components/settings_v2/extensions/utils.ts b/ui/desktop/src/components/settings_v2/extensions/utils.ts index 45cd7ad8..596cada2 100644 --- a/ui/desktop/src/components/settings_v2/extensions/utils.ts +++ b/ui/desktop/src/components/settings_v2/extensions/utils.ts @@ -127,3 +127,19 @@ export function extractExtensionConfig(fixedEntry: FixedExtensionEntry): Extensi const { enabled, ...extensionConfig } = fixedEntry; return extensionConfig; } + +export async function replaceWithShims(cmd: string) { + const binaryPathMap: Record = { + goosed: await window.electron.getBinaryPath('goosed'), + jbang: await window.electron.getBinaryPath('jbang'), + npx: await window.electron.getBinaryPath('npx'), + uvx: await window.electron.getBinaryPath('uvx'), + }; + + if (binaryPathMap[cmd]) { + console.log('--------> Replacing command with shim ------>', cmd, binaryPathMap[cmd]); + cmd = binaryPathMap[cmd]; + } + + return cmd; +} diff --git a/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx b/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx index 8333c398..71a3db8f 100644 --- a/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx +++ b/ui/desktop/src/components/settings_v2/providers/ProviderSettingsPage.tsx @@ -4,7 +4,7 @@ import BackButton from '../../ui/BackButton'; import ProviderGrid from './ProviderGrid'; import { useConfig } from '../../ConfigContext'; import { ProviderDetails } from '../../../api/types.gen'; -import { useAgent } from '../../../agent/UpdateAgent'; +import { initializeAgent } from '../../../agent/'; import WelcomeGooseLogo from '../../WelcomeGooseLogo'; interface ProviderSettingsProps { @@ -14,7 +14,6 @@ interface ProviderSettingsProps { export default function ProviderSettings({ onClose, isOnboarding }: ProviderSettingsProps) { const { getProviders, upsert } = useConfig(); - const { initializeAgent } = useAgent(); const [loading, setLoading] = useState(true); const [providers, setProviders] = useState([]); const initialLoadDone = useRef(false); @@ -70,7 +69,7 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett ); // initialize agent - await initializeAgent(provider_name, model); + await initializeAgent({ provider: provider.name, model }); } catch (error) { console.error(`Failed to initialize with provider ${provider_name}:`, error); } diff --git a/ui/desktop/src/utils/providerUtils.ts b/ui/desktop/src/utils/providerUtils.ts index e5e9ee61..6c9356f8 100644 --- a/ui/desktop/src/utils/providerUtils.ts +++ b/ui/desktop/src/utils/providerUtils.ts @@ -3,6 +3,7 @@ import { loadAndAddStoredExtensions } from '../extensions'; import { GOOSE_PROVIDER, GOOSE_MODEL } from '../env_vars'; import { Model } from '../components/settings/models/ModelContext'; import { gooseModels } from '../components/settings/models/GooseModels'; +import { initializeAgent } from '../agent'; export function getStoredProvider(config: any): string | null { return config.GOOSE_PROVIDER || localStorage.getItem(GOOSE_PROVIDER); @@ -32,23 +33,6 @@ export interface Provider { requiredKeys: string[]; // List of required keys } -const addAgent = async (provider: string, model: string) => { - const response = await fetch(getApiUrl('/agent'), { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Secret-Key': getSecretKey(), - }, - body: JSON.stringify({ provider: provider, model: model }), - }); - - if (!response.ok) { - throw new Error(`Failed to add agent: ${response.statusText}`); - } - - return response; -}; - // Desktop-specific system prompt extension const desktopPrompt = `You are being accessed through the Goose Desktop application. @@ -84,7 +68,7 @@ There may be (but not always) some tools mentioned in the instructions which you export const initializeSystem = async (provider: string, model: string) => { try { console.log('initializing agent with provider', provider, 'model', model); - await addAgent(provider.toLowerCase().replace(/ /g, '_'), model); + await initializeAgent({ provider, model }); // Sync the model state with React const syncedModel = syncModelWithAgent(provider, model); @@ -119,7 +103,7 @@ export const initializeSystem = async (provider: string, model: string) => { // This will go away after the release of settings v2 as we now handle this via // - // initializeBuildInExtensions + // initializeBuiltInExtensions // syncBuiltInExtensions if (!process.env.ALPHA) { loadAndAddStoredExtensions().catch((error) => {