chore: Initial pass at cleanup of initializeAgent (#1888)
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
"license": {
|
"license": {
|
||||||
"name": "Apache-2.0"
|
"name": "Apache-2.0"
|
||||||
},
|
},
|
||||||
"version": "1.0.15"
|
"version": "1.0.16"
|
||||||
},
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"/config": {
|
"/config": {
|
||||||
|
|||||||
@@ -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<Response> => {
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -15,19 +15,3 @@ export async function initializeAgent(model: string, provider: string) {
|
|||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function replaceWithShims(cmd: string) {
|
|
||||||
const binaryPathMap: Record<string, string> = {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ExtensionConfig } from '../../../api/types.gen';
|
import { ExtensionConfig } from '../../../api/types.gen';
|
||||||
import { getApiUrl, getSecretKey } from '../../../config';
|
import { getApiUrl, getSecretKey } from '../../../config';
|
||||||
import { toastService, ToastServiceOptions } from '../../../toasts';
|
import { toastService, ToastServiceOptions } from '../../../toasts';
|
||||||
|
import { replaceWithShims } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes an API call to the extension endpoints
|
* Makes an API call to the extension endpoints
|
||||||
@@ -166,19 +167,3 @@ export async function removeFromAgent(
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the path to the binary based on the command
|
|
||||||
async function replaceWithShims(cmd: string): Promise<string> {
|
|
||||||
const binaryPathMap: Record<string, string> = {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -127,3 +127,19 @@ export function extractExtensionConfig(fixedEntry: FixedExtensionEntry): Extensi
|
|||||||
const { enabled, ...extensionConfig } = fixedEntry;
|
const { enabled, ...extensionConfig } = fixedEntry;
|
||||||
return extensionConfig;
|
return extensionConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function replaceWithShims(cmd: string) {
|
||||||
|
const binaryPathMap: Record<string, string> = {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import BackButton from '../../ui/BackButton';
|
|||||||
import ProviderGrid from './ProviderGrid';
|
import ProviderGrid from './ProviderGrid';
|
||||||
import { useConfig } from '../../ConfigContext';
|
import { useConfig } from '../../ConfigContext';
|
||||||
import { ProviderDetails } from '../../../api/types.gen';
|
import { ProviderDetails } from '../../../api/types.gen';
|
||||||
import { useAgent } from '../../../agent/UpdateAgent';
|
import { initializeAgent } from '../../../agent/';
|
||||||
import WelcomeGooseLogo from '../../WelcomeGooseLogo';
|
import WelcomeGooseLogo from '../../WelcomeGooseLogo';
|
||||||
|
|
||||||
interface ProviderSettingsProps {
|
interface ProviderSettingsProps {
|
||||||
@@ -14,7 +14,6 @@ interface ProviderSettingsProps {
|
|||||||
|
|
||||||
export default function ProviderSettings({ onClose, isOnboarding }: ProviderSettingsProps) {
|
export default function ProviderSettings({ onClose, isOnboarding }: ProviderSettingsProps) {
|
||||||
const { getProviders, upsert } = useConfig();
|
const { getProviders, upsert } = useConfig();
|
||||||
const { initializeAgent } = useAgent();
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [providers, setProviders] = useState<ProviderDetails[]>([]);
|
const [providers, setProviders] = useState<ProviderDetails[]>([]);
|
||||||
const initialLoadDone = useRef(false);
|
const initialLoadDone = useRef(false);
|
||||||
@@ -70,7 +69,7 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
|||||||
);
|
);
|
||||||
|
|
||||||
// initialize agent
|
// initialize agent
|
||||||
await initializeAgent(provider_name, model);
|
await initializeAgent({ provider: provider.name, model });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to initialize with provider ${provider_name}:`, error);
|
console.error(`Failed to initialize with provider ${provider_name}:`, error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { loadAndAddStoredExtensions } from '../extensions';
|
|||||||
import { GOOSE_PROVIDER, GOOSE_MODEL } from '../env_vars';
|
import { GOOSE_PROVIDER, GOOSE_MODEL } from '../env_vars';
|
||||||
import { Model } from '../components/settings/models/ModelContext';
|
import { Model } from '../components/settings/models/ModelContext';
|
||||||
import { gooseModels } from '../components/settings/models/GooseModels';
|
import { gooseModels } from '../components/settings/models/GooseModels';
|
||||||
|
import { initializeAgent } from '../agent';
|
||||||
|
|
||||||
export function getStoredProvider(config: any): string | null {
|
export function getStoredProvider(config: any): string | null {
|
||||||
return config.GOOSE_PROVIDER || localStorage.getItem(GOOSE_PROVIDER);
|
return config.GOOSE_PROVIDER || localStorage.getItem(GOOSE_PROVIDER);
|
||||||
@@ -32,23 +33,6 @@ export interface Provider {
|
|||||||
requiredKeys: string[]; // List of required keys
|
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
|
// Desktop-specific system prompt extension
|
||||||
const desktopPrompt = `You are being accessed through the Goose Desktop application.
|
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) => {
|
export const initializeSystem = async (provider: string, model: string) => {
|
||||||
try {
|
try {
|
||||||
console.log('initializing agent with provider', provider, 'model', model);
|
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
|
// Sync the model state with React
|
||||||
const syncedModel = syncModelWithAgent(provider, model);
|
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
|
// This will go away after the release of settings v2 as we now handle this via
|
||||||
//
|
//
|
||||||
// initializeBuildInExtensions
|
// initializeBuiltInExtensions
|
||||||
// syncBuiltInExtensions
|
// syncBuiltInExtensions
|
||||||
if (!process.env.ALPHA) {
|
if (!process.env.ALPHA) {
|
||||||
loadAndAddStoredExtensions().catch((error) => {
|
loadAndAddStoredExtensions().catch((error) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user