mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-19 15:14:21 +01:00
add copy button and make extension builtins any time (#1961)
This commit is contained in:
@@ -63,7 +63,7 @@ export default function App() {
|
|||||||
view: 'welcome',
|
view: 'welcome',
|
||||||
viewOptions: {},
|
viewOptions: {},
|
||||||
});
|
});
|
||||||
const { getExtensions, addExtension, read } = useConfig();
|
const { getExtensions, addExtension, read, upsert } = useConfig();
|
||||||
const initAttemptedRef = useRef(false);
|
const initAttemptedRef = useRef(false);
|
||||||
|
|
||||||
// Utility function to extract the command from the link
|
// Utility function to extract the command from the link
|
||||||
@@ -91,6 +91,7 @@ export default function App() {
|
|||||||
const initializeApp = async () => {
|
const initializeApp = async () => {
|
||||||
try {
|
try {
|
||||||
const config = window.electron.getConfig();
|
const config = window.electron.getConfig();
|
||||||
|
|
||||||
const provider = config.GOOSE_PROVIDER ?? (await read('GOOSE_PROVIDER', false));
|
const provider = config.GOOSE_PROVIDER ?? (await read('GOOSE_PROVIDER', false));
|
||||||
const model = config.GOOSE_MODEL ?? (await read('GOOSE_MODEL', false));
|
const model = config.GOOSE_MODEL ?? (await read('GOOSE_MODEL', false));
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { ChatSmart, Idea, More, Refresh, Time, Send } from '../icons';
|
|||||||
import { FolderOpen, Moon, Sliders, Sun } from 'lucide-react';
|
import { FolderOpen, Moon, Sliders, Sun } from 'lucide-react';
|
||||||
import { View } from '../../App';
|
import { View } from '../../App';
|
||||||
import { useConfig } from '../ConfigContext';
|
import { useConfig } from '../ConfigContext';
|
||||||
|
import { toastService } from '../../toasts';
|
||||||
|
|
||||||
interface VersionInfo {
|
interface VersionInfo {
|
||||||
current_version: string;
|
current_version: string;
|
||||||
@@ -262,7 +263,7 @@ export default function MoreMenu({
|
|||||||
await remove('GOOSE_PROVIDER', false);
|
await remove('GOOSE_PROVIDER', false);
|
||||||
await remove('GOOSE_MODEL', false);
|
await remove('GOOSE_MODEL', false);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
window.electron.createChatWindow();
|
setView('welcome');
|
||||||
}}
|
}}
|
||||||
danger
|
danger
|
||||||
subtitle="Clear selected model and restart (alpha)"
|
subtitle="Clear selected model and restart (alpha)"
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export async function extensionApiCall(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Final catch-all error handler
|
// Final catch-all error handler
|
||||||
toastService.dismiss(toastId);
|
toastService.dismiss(toastId);
|
||||||
const msg = error.length < 100 ? error : `Failed to ${action.presentTense} extension`;
|
const msg = error.length < 70 ? error : `Failed to ${action.presentTense} extension`;
|
||||||
toastService.error({
|
toastService.error({
|
||||||
title: extensionName,
|
title: extensionName,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
import type { ExtensionConfig } from '../../../api/types.gen';
|
import type { ExtensionConfig } from '../../../api/types.gen';
|
||||||
import { FixedExtensionEntry } from '../../ConfigContext';
|
import { FixedExtensionEntry } from '../../ConfigContext';
|
||||||
import builtInExtensionsData from './built-in-extensions.json';
|
import bundledExtensionsData from './bundled-extensions.json';
|
||||||
import { nameToKey } from './utils';
|
import { nameToKey } from './utils';
|
||||||
|
|
||||||
// Type definition for built-in extensions from JSON
|
// Type definition for built-in extensions from JSON
|
||||||
type BuiltinExtension = {
|
type BundledExtension = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
display_name: string;
|
display_name?: string;
|
||||||
description: string;
|
description?: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
type: 'builtin';
|
type: 'builtin' | 'stdio' | 'sse';
|
||||||
|
cmd?: string;
|
||||||
|
args?: string[];
|
||||||
|
uri?: string;
|
||||||
envs?: { [key: string]: string };
|
envs?: { [key: string]: string };
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
allow_configure?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,43 +28,61 @@ type BuiltinExtension = {
|
|||||||
* @param addExtensionFn Function to add a new extension to the config
|
* @param addExtensionFn Function to add a new extension to the config
|
||||||
* @returns Promise that resolves when sync is complete
|
* @returns Promise that resolves when sync is complete
|
||||||
*/
|
*/
|
||||||
export async function syncBuiltInExtensions(
|
export async function syncBundledExtensions(
|
||||||
existingExtensions: FixedExtensionEntry[],
|
existingExtensions: FixedExtensionEntry[],
|
||||||
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
|
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
console.log('Setting up built-in extensions... in syncBuiltinExtensions');
|
|
||||||
|
|
||||||
// Create a set of existing extension IDs for quick lookup
|
// Create a set of existing extension IDs for quick lookup
|
||||||
const existingExtensionKeys = new Set(existingExtensions.map((ext) => nameToKey(ext.name)));
|
const existingExtensionKeys = new Set(existingExtensions.map((ext) => nameToKey(ext.name)));
|
||||||
console.log('existing extension ids', existingExtensionKeys);
|
|
||||||
|
|
||||||
// Cast the imported JSON data to the expected type
|
// Cast the imported JSON data to the expected type
|
||||||
const builtinExtensions = builtInExtensionsData as BuiltinExtension[];
|
const bundledExtensions = bundledExtensionsData as BundledExtension[];
|
||||||
|
|
||||||
// Track how many extensions were added
|
// Track how many extensions were added
|
||||||
let addedCount = 0;
|
let addedCount = 0;
|
||||||
|
|
||||||
// Check each built-in extension
|
// Check each built-in extension
|
||||||
for (const builtinExt of builtinExtensions) {
|
for (const bundledExt of bundledExtensions) {
|
||||||
// Only add if the extension doesn't already exist -- use the id
|
// Only add if the extension doesn't already exist -- use the id
|
||||||
if (!existingExtensionKeys.has(builtinExt.id)) {
|
if (!existingExtensionKeys.has(bundledExt.id)) {
|
||||||
console.log(`Adding built-in extension: ${builtinExt.id}`);
|
console.log(`Adding built-in extension: ${bundledExt.id}`);
|
||||||
|
let extConfig: ExtensionConfig;
|
||||||
// Convert to the ExtensionConfig format
|
switch (bundledExt.type) {
|
||||||
const extConfig: ExtensionConfig = {
|
case 'builtin':
|
||||||
name: builtinExt.name,
|
extConfig = {
|
||||||
display_name: builtinExt.display_name,
|
name: bundledExt.name,
|
||||||
type: 'builtin',
|
display_name: bundledExt.display_name,
|
||||||
timeout: builtinExt.timeout ?? 300,
|
type: bundledExt.type,
|
||||||
};
|
timeout: bundledExt.timeout ?? 300,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'stdio':
|
||||||
|
extConfig = {
|
||||||
|
name: bundledExt.name,
|
||||||
|
description: bundledExt.description,
|
||||||
|
type: bundledExt.type,
|
||||||
|
timeout: bundledExt.timeout,
|
||||||
|
cmd: bundledExt.cmd,
|
||||||
|
args: bundledExt.args,
|
||||||
|
envs: bundledExt.envs,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'sse':
|
||||||
|
extConfig = {
|
||||||
|
name: bundledExt.name,
|
||||||
|
description: bundledExt.description,
|
||||||
|
type: bundledExt.type,
|
||||||
|
timeout: bundledExt.timeout,
|
||||||
|
uri: bundledExt.uri,
|
||||||
|
};
|
||||||
|
}
|
||||||
// Add the extension with its default enabled state
|
// Add the extension with its default enabled state
|
||||||
try {
|
try {
|
||||||
await addExtensionFn(nameToKey(builtinExt.name), extConfig, builtinExt.enabled);
|
await addExtensionFn(bundledExt.name, extConfig, bundledExt.enabled);
|
||||||
addedCount++;
|
addedCount++;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to add built-in extension ${builtinExt.name}:`, error);
|
console.error(`Failed to add built-in extension ${bundledExt.name}:`, error);
|
||||||
// Continue with other extensions even if one fails
|
// Continue with other extensions even if one fails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,9 +103,9 @@ export async function syncBuiltInExtensions(
|
|||||||
* Function to initialize all built-in extensions for a first-time user.
|
* Function to initialize all built-in extensions for a first-time user.
|
||||||
* This can be called when the application is first installed.
|
* This can be called when the application is first installed.
|
||||||
*/
|
*/
|
||||||
export async function initializeBuiltInExtensions(
|
export async function initializeBundledExtensions(
|
||||||
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
|
addExtensionFn: (name: string, config: ExtensionConfig, enabled: boolean) => Promise<void>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Call with an empty list to ensure all built-ins are added
|
// Call with an empty list to ensure all built-ins are added
|
||||||
await syncBuiltInExtensions([], addExtensionFn);
|
await syncBundledExtensions([], addExtensionFn);
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ export {
|
|||||||
} from './extension-manager';
|
} from './extension-manager';
|
||||||
|
|
||||||
// Export built-in extension functions
|
// Export built-in extension functions
|
||||||
export { syncBuiltInExtensions, initializeBuiltInExtensions } from './built-in';
|
export { syncBundledExtensions, initializeBundledExtensions } from './bundled-extensions';
|
||||||
|
|
||||||
// Export deeplink handling
|
// Export deeplink handling
|
||||||
export { addExtensionFromDeepLink } from './deeplink';
|
export { addExtensionFromDeepLink } from './deeplink';
|
||||||
|
|||||||
@@ -4,13 +4,12 @@ import ModelSettingsButtons from './subcomponents/ModelSettingsButtons';
|
|||||||
import { useConfig } from '../../ConfigContext';
|
import { useConfig } from '../../ConfigContext';
|
||||||
import { toastError } from '../../../toasts';
|
import { toastError } from '../../../toasts';
|
||||||
|
|
||||||
|
import { UNKNOWN_PROVIDER_MSG, UNKNOWN_PROVIDER_TITLE } from './index';
|
||||||
|
|
||||||
interface ModelsSectionProps {
|
interface ModelsSectionProps {
|
||||||
setView: (view: View) => void;
|
setView: (view: View) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UNKNOWN_PROVIDER_TITLE = 'Provider name error';
|
|
||||||
const UNKNOWN_PROVIDER_MSG = 'Unknown provider in config -- please inspect your config.yaml';
|
|
||||||
|
|
||||||
export default function ModelsSection({ setView }: ModelsSectionProps) {
|
export default function ModelsSection({ setView }: ModelsSectionProps) {
|
||||||
const [provider, setProvider] = useState<string | null>(null);
|
const [provider, setProvider] = useState<string | null>(null);
|
||||||
const [model, setModel] = useState<string>('');
|
const [model, setModel] = useState<string>('');
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import type { ExtensionConfig, FixedExtensionEntry } from '../../ConfigContext';
|
|||||||
// titles
|
// titles
|
||||||
const CHANGE_MODEL_TOAST_TITLE = 'Model selected';
|
const CHANGE_MODEL_TOAST_TITLE = 'Model selected';
|
||||||
const START_AGENT_TITLE = 'Initialize agent';
|
const START_AGENT_TITLE = 'Initialize agent';
|
||||||
const UNKNOWN_PROVIDER_TITLE = 'Provider name lookup';
|
export const UNKNOWN_PROVIDER_TITLE = 'Provider name lookup';
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
const SWITCH_MODEL_AGENT_ERROR_MSG = 'Failed to start agent with selected model';
|
const SWITCH_MODEL_AGENT_ERROR_MSG = 'Failed to start agent with selected model';
|
||||||
const CONFIG_UPDATE_ERROR_MSG = 'Failed to update configuration settings';
|
const CONFIG_UPDATE_ERROR_MSG = 'Failed to update configuration settings';
|
||||||
const CONFIG_READ_MODEL_ERROR_MSG = 'Failed to read GOOSE_MODEL or GOOSE_PROVIDER from config';
|
const CONFIG_READ_MODEL_ERROR_MSG = 'Failed to read GOOSE_MODEL or GOOSE_PROVIDER from config';
|
||||||
const UNKNOWN_PROVIDER_MSG = 'Unknown provider in config -- please inspect your config.yaml';
|
export const UNKNOWN_PROVIDER_MSG = 'Unknown provider in config -- please inspect your config.yaml';
|
||||||
|
|
||||||
// success
|
// success
|
||||||
const SWITCH_MODEL_SUCCESS_MSG = 'Successfully switched models';
|
const SWITCH_MODEL_SUCCESS_MSG = 'Successfully switched models';
|
||||||
@@ -114,11 +114,6 @@ export async function getCurrentModelAndProviderForDisplay({
|
|||||||
try {
|
try {
|
||||||
metadata = await getProviderMetadata(gooseProvider, getProviders);
|
metadata = await getProviderMetadata(gooseProvider, getProviders);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toastError({
|
|
||||||
title: UNKNOWN_PROVIDER_TITLE,
|
|
||||||
msg: UNKNOWN_PROVIDER_MSG,
|
|
||||||
traceback: error,
|
|
||||||
});
|
|
||||||
return { model: gooseModel, provider: gooseProvider };
|
return { model: gooseModel, provider: gooseProvider };
|
||||||
}
|
}
|
||||||
const providerDisplayName = metadata.display_name;
|
const providerDisplayName = metadata.display_name;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { useConfig } from '../../ConfigContext';
|
|||||||
import { ProviderDetails } from '../../../api/types.gen';
|
import { ProviderDetails } from '../../../api/types.gen';
|
||||||
import { initializeSystem } from '../../../utils/providerUtils';
|
import { initializeSystem } from '../../../utils/providerUtils';
|
||||||
import WelcomeGooseLogo from '../../WelcomeGooseLogo';
|
import WelcomeGooseLogo from '../../WelcomeGooseLogo';
|
||||||
|
import { toastService } from '../../../toasts';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
interface ProviderSettingsProps {
|
interface ProviderSettingsProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -56,7 +58,6 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
|||||||
const provider_name = provider.name;
|
const provider_name = provider.name;
|
||||||
const model = provider.metadata.default_model;
|
const model = provider.metadata.default_model;
|
||||||
|
|
||||||
console.log(`Launching with provider: ${provider.name}`);
|
|
||||||
try {
|
try {
|
||||||
// update the config
|
// update the config
|
||||||
// set GOOSE_PROVIDER in the config file
|
// set GOOSE_PROVIDER in the config file
|
||||||
@@ -76,6 +77,11 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to initialize with provider ${provider_name}:`, error);
|
console.error(`Failed to initialize with provider ${provider_name}:`, error);
|
||||||
}
|
}
|
||||||
|
toastService.configure({ silent: false });
|
||||||
|
toastService.success({
|
||||||
|
title: 'Success!',
|
||||||
|
msg: `Started goose with ${model} by ${provider.metadata.display_name}. You can change the model via the lower right corner.`,
|
||||||
|
});
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
[onClose, upsert]
|
[onClose, upsert]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { toast, ToastOptions } from 'react-toastify';
|
import { toast, ToastOptions } from 'react-toastify';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Button } from './components/ui/button';
|
||||||
|
|
||||||
export interface ToastServiceOptions {
|
export interface ToastServiceOptions {
|
||||||
silent?: boolean;
|
silent?: boolean;
|
||||||
@@ -123,12 +124,12 @@ export function toastError({ title, msg, traceback, toastOptions }: ToastErrorPr
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-none flex items-center">
|
<div className="flex-none flex items-center">
|
||||||
{traceback ? (
|
{traceback ? (
|
||||||
<button
|
<Button
|
||||||
className="text-textProminentInverse font-medium"
|
className="text-textProminentInverse font-medium rt-variant-outline dark:bg-gray-300 bg-slate"
|
||||||
onClick={() => navigator.clipboard.writeText(traceback)}
|
onClick={() => navigator.clipboard.writeText(traceback)}
|
||||||
>
|
>
|
||||||
Copy error
|
Copy error
|
||||||
</button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
</div>,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ 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';
|
import { initializeAgent } from '../agent';
|
||||||
import {
|
import {
|
||||||
initializeBuiltInExtensions,
|
initializeBundledExtensions,
|
||||||
syncBuiltInExtensions,
|
syncBundledExtensions,
|
||||||
addToAgentOnStartup,
|
addToAgentOnStartup,
|
||||||
} from '../components/settings_v2/extensions';
|
} from '../components/settings_v2/extensions';
|
||||||
import { extractExtensionConfig } from '../components/settings_v2/extensions/utils';
|
import { extractExtensionConfig } from '../components/settings_v2/extensions/utils';
|
||||||
@@ -128,10 +128,10 @@ export const initializeSystem = async (
|
|||||||
let refreshedExtensions = await options.getExtensions(false);
|
let refreshedExtensions = await options.getExtensions(false);
|
||||||
|
|
||||||
if (refreshedExtensions.length === 0) {
|
if (refreshedExtensions.length === 0) {
|
||||||
await initializeBuiltInExtensions(options.addExtension);
|
await initializeBundledExtensions(options.addExtension);
|
||||||
refreshedExtensions = await options.getExtensions(false);
|
refreshedExtensions = await options.getExtensions(false);
|
||||||
} else {
|
} else {
|
||||||
await syncBuiltInExtensions(refreshedExtensions, options.addExtension);
|
await syncBundledExtensions(refreshedExtensions, options.addExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add enabled extensions to agent
|
// Add enabled extensions to agent
|
||||||
|
|||||||
Reference in New Issue
Block a user