Move token limits to backend (#2484)
This commit is contained in:
+17
-309
@@ -1,26 +1,18 @@
|
||||
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { IpcRendererEvent } from 'electron';
|
||||
import { addExtensionFromDeepLink } from './extensions';
|
||||
import { openSharedSessionFromDeepLink } from './sessionLinks';
|
||||
import { getStoredModel } from './utils/providerUtils';
|
||||
import { getStoredProvider, initializeSystem } from './utils/providerUtils';
|
||||
import { useModel } from './components/settings/models/ModelContext';
|
||||
import { useRecentModels } from './components/settings/models/RecentModels';
|
||||
import { createSelectedModel } from './components/settings/models/utils';
|
||||
import { getDefaultModel } from './components/settings/models/hardcoded_stuff';
|
||||
import { initializeSystem } from './utils/providerUtils';
|
||||
import { ErrorUI } from './components/ErrorBoundary';
|
||||
import { ConfirmationModal } from './components/ui/ConfirmationModal';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import { toastService } from './toasts';
|
||||
import { settingsV2Enabled } from './flags';
|
||||
import { extractExtensionName } from './components/settings/extensions/utils';
|
||||
import { GoosehintsModal } from './components/GoosehintsModal';
|
||||
import { SessionDetails } from './sessions';
|
||||
|
||||
import WelcomeView from './components/WelcomeView';
|
||||
import ChatView from './components/ChatView';
|
||||
import SuspenseLoader from './suspense-loader';
|
||||
import SettingsView, { type SettingsViewOptions } from './components/settings/SettingsView';
|
||||
import { type SettingsViewOptions } from './components/settings/SettingsView';
|
||||
import SettingsViewV2 from './components/settings_v2/SettingsView';
|
||||
import MoreModelsView from './components/settings/models/MoreModelsView';
|
||||
import ConfigureProvidersView from './components/settings/providers/ConfigureProvidersView';
|
||||
@@ -29,7 +21,6 @@ import SharedSessionView from './components/sessions/SharedSessionView';
|
||||
import ProviderSettings from './components/settings_v2/providers/ProviderSettingsPage';
|
||||
import RecipeEditor from './components/RecipeEditor';
|
||||
import { useChat } from './hooks/useChat';
|
||||
import { addExtension as addExtensionDirect, FullExtensionConfig } from './extensions';
|
||||
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import { useConfig, MalformedConfigError } from './components/ConfigContext';
|
||||
@@ -101,7 +92,7 @@ export default function App() {
|
||||
const [extensionConfirmLabel, setExtensionConfirmLabel] = useState<string>('');
|
||||
const [extensionConfirmTitle, setExtensionConfirmTitle] = useState<string>('');
|
||||
const [{ view, viewOptions }, setInternalView] = useState<ViewConfig>(getInitialView());
|
||||
const { getExtensions, addExtension, disableAllExtensions, read } = useConfig();
|
||||
const { getExtensions, addExtension, read } = useConfig();
|
||||
const initAttemptedRef = useRef(false);
|
||||
|
||||
// Utility function to extract the command from the link
|
||||
@@ -123,158 +114,7 @@ export default function App() {
|
||||
setInternalView({ view, viewOptions });
|
||||
};
|
||||
|
||||
const disableAllStoredExtensions = () => {
|
||||
const userSettingsStr = localStorage.getItem('user_settings');
|
||||
if (!userSettingsStr) return;
|
||||
|
||||
try {
|
||||
const userSettings = JSON.parse(userSettingsStr);
|
||||
// Store original state before modifying
|
||||
localStorage.setItem('user_settings_backup', userSettingsStr);
|
||||
console.log('Backing up user_settings');
|
||||
|
||||
// Disable all extensions
|
||||
userSettings.extensions = userSettings.extensions.map((ext) => ({
|
||||
...ext,
|
||||
enabled: false,
|
||||
}));
|
||||
|
||||
localStorage.setItem('user_settings', JSON.stringify(userSettings));
|
||||
console.log('Disabled all stored extensions');
|
||||
window.electron.emit('settings-updated');
|
||||
} catch (error) {
|
||||
console.error('Error disabling stored extensions:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Function to restore original extension states for new non-recipe windows
|
||||
const restoreOriginalExtensionStates = () => {
|
||||
const backupStr = localStorage.getItem('user_settings_backup');
|
||||
if (backupStr) {
|
||||
localStorage.setItem('user_settings', backupStr);
|
||||
console.log('Restored original extension states');
|
||||
}
|
||||
};
|
||||
|
||||
const updateUserSettingsWithConfig = (extensions: FullExtensionConfig[]) => {
|
||||
try {
|
||||
const userSettingsStr = localStorage.getItem('user_settings');
|
||||
const userSettings = userSettingsStr ? JSON.parse(userSettingsStr) : { extensions: [] };
|
||||
|
||||
// For each extension in the passed in config
|
||||
extensions.forEach((newExtension) => {
|
||||
// Find if this extension already exists
|
||||
const existingIndex = userSettings.extensions.findIndex(
|
||||
(ext) => ext.id === newExtension.id
|
||||
);
|
||||
|
||||
if (existingIndex !== -1) {
|
||||
// Extension exists - just set its enabled to true
|
||||
userSettings.extensions[existingIndex].enabled = true;
|
||||
} else {
|
||||
// Extension is new - add it to the array
|
||||
userSettings.extensions.push({
|
||||
...newExtension,
|
||||
enabled: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
localStorage.setItem('user_settings', JSON.stringify(userSettings));
|
||||
console.log('Updated user settings with new/enabled extensions:', userSettings.extensions);
|
||||
|
||||
// Notify any listeners (like the settings page) that settings have changed
|
||||
window.electron.emit('settings-updated');
|
||||
} catch (error) {
|
||||
console.error('Error updating user settings:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const enableRecipeConfigExtensions = async (extensions: FullExtensionConfig[]) => {
|
||||
if (!extensions?.length) {
|
||||
console.log('No extensions to enable from bot config');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Enabling ${extensions.length} extensions from bot config:`, extensions);
|
||||
|
||||
disableAllStoredExtensions();
|
||||
|
||||
// Wait for initial server readiness
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
for (const extension of extensions) {
|
||||
try {
|
||||
console.log(`Enabling extension: ${extension.name}`);
|
||||
const extensionConfig = {
|
||||
...extension,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
// Try to add the extension
|
||||
const response = await addExtensionDirect(extensionConfig, false);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(
|
||||
`Failed to enable extension ${extension.name}: Server returned ${response.status}`
|
||||
);
|
||||
// If it's a 428, retry once
|
||||
if (response.status === 428) {
|
||||
console.log('Server not ready, waiting and will retry...');
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
try {
|
||||
await addExtensionDirect(extensionConfig, true);
|
||||
console.log(`Successfully enabled extension ${extension.name} on retry`);
|
||||
} catch (retryError) {
|
||||
console.error(`Failed to enable extension ${extension.name} on retry:`, retryError);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
updateUserSettingsWithConfig(extensions);
|
||||
|
||||
console.log(`Successfully enabled extension: ${extension.name}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to enable extension ${extension.name}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Finished enabling bot config extensions');
|
||||
};
|
||||
|
||||
const _enableRecipeConfigExtensionsV2 = useCallback(
|
||||
async (extensions: FullExtensionConfig[]) => {
|
||||
if (!extensions?.length) {
|
||||
console.log('No extensions to enable from bot config');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await disableAllExtensions();
|
||||
console.log('Disabled all existing extensions');
|
||||
|
||||
for (const extension of extensions) {
|
||||
try {
|
||||
console.log(`Enabling extension: ${extension.name}`);
|
||||
await addExtension(extension.name, extension, true);
|
||||
} catch (error) {
|
||||
console.error(`Failed to enable extension ${extension.name}:`, error);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to enable bot extensions');
|
||||
}
|
||||
console.log('Finished enabling bot config extensions');
|
||||
},
|
||||
[disableAllExtensions, addExtension]
|
||||
);
|
||||
|
||||
// settings v2 initialization
|
||||
useEffect(() => {
|
||||
if (!settingsV2Enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Guard against multiple initialization attempts
|
||||
if (initAttemptedRef.current) {
|
||||
console.log('Initialization already attempted, skipping...');
|
||||
@@ -326,12 +166,6 @@ export default function App() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle bot config extensions first
|
||||
// if (recipeConfig?.extensions?.length > 0 && viewType != 'recipeEditor') {
|
||||
// console.log('Found extensions in bot config:', recipeConfig.extensions);
|
||||
// await enableRecipeConfigExtensionsV2(recipeConfig.extensions);
|
||||
// }
|
||||
|
||||
const config = window.electron.getConfig();
|
||||
|
||||
const provider = (await read('GOOSE_PROVIDER', false)) ?? config.GOOSE_DEFAULT_PROVIDER;
|
||||
@@ -622,11 +456,7 @@ export default function App() {
|
||||
console.log(`Confirming installation of extension from: ${pendingLink}`);
|
||||
setModalVisible(false); // Dismiss modal immediately
|
||||
try {
|
||||
if (settingsV2Enabled) {
|
||||
await addExtensionFromDeepLinkV2(pendingLink, addExtension, setView);
|
||||
} else {
|
||||
await addExtensionFromDeepLink(pendingLink, setView);
|
||||
}
|
||||
await addExtensionFromDeepLinkV2(pendingLink, addExtension, setView);
|
||||
console.log('Extension installation successful');
|
||||
} catch (error) {
|
||||
console.error('Failed to add extension:', error);
|
||||
@@ -648,112 +478,6 @@ export default function App() {
|
||||
setPendingLink(null);
|
||||
};
|
||||
|
||||
// TODO: remove -- careful removal of these and the useEffect below breaks
|
||||
// reloading to chat view using stored provider
|
||||
const { switchModel } = useModel(); // TODO: remove
|
||||
const { addRecentModel } = useRecentModels(); // TODO: remove
|
||||
|
||||
useEffect(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const viewType = urlParams.get('view');
|
||||
const recipeConfig = window.appConfig.get('recipeConfig');
|
||||
|
||||
if (settingsV2Enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Initializing app with settings v1`);
|
||||
|
||||
// Handle bot config extensions first
|
||||
if (recipeConfig?.extensions?.length > 0 && viewType != 'recipeEditor') {
|
||||
console.log('Found extensions in bot config:', recipeConfig.extensions);
|
||||
enableRecipeConfigExtensions(recipeConfig.extensions);
|
||||
}
|
||||
|
||||
// If we have a specific view type in the URL, use that and skip provider detection
|
||||
if (viewType) {
|
||||
if (viewType === 'recipeEditor' && recipeConfig) {
|
||||
console.log('Setting view to recipeEditor with config:', recipeConfig);
|
||||
setView('recipeEditor', { config: recipeConfig });
|
||||
} else {
|
||||
setView(viewType as View);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if not in any of the states above (in a regular chat)
|
||||
if (!recipeConfig) {
|
||||
restoreOriginalExtensionStates();
|
||||
}
|
||||
|
||||
console.log(`Initializing app with settings v1`);
|
||||
|
||||
// Attempt to detect config for a stored provider
|
||||
const detectStoredProvider = () => {
|
||||
try {
|
||||
const config = window.electron.getConfig();
|
||||
console.log('Loaded config:', JSON.stringify(config));
|
||||
|
||||
const storedProvider = getStoredProvider(config);
|
||||
console.log('Stored provider:', storedProvider);
|
||||
|
||||
if (storedProvider) {
|
||||
setView('chat');
|
||||
} else {
|
||||
setView('welcome');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('DETECTION ERROR:', error);
|
||||
setFatalError(`Config detection error: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize system if we have a stored provider
|
||||
const setupStoredProvider = async () => {
|
||||
try {
|
||||
const config = window.electron.getConfig();
|
||||
|
||||
if (config.GOOSE_PROVIDER && config.GOOSE_MODEL) {
|
||||
console.log('using GOOSE_PROVIDER and GOOSE_MODEL from config');
|
||||
await initializeSystem(config.GOOSE_PROVIDER, config.GOOSE_MODEL);
|
||||
return;
|
||||
}
|
||||
|
||||
const storedProvider = getStoredProvider(config);
|
||||
const storedModel = getStoredModel();
|
||||
|
||||
if (storedProvider) {
|
||||
try {
|
||||
await initializeSystem(storedProvider, storedModel);
|
||||
console.log('Setup using locally stored provider:', storedProvider);
|
||||
console.log('Setup using locally stored model:', storedModel);
|
||||
|
||||
if (!storedModel) {
|
||||
const modelName = getDefaultModel(storedProvider.toLowerCase());
|
||||
const model = createSelectedModel(storedProvider.toLowerCase(), modelName);
|
||||
switchModel(model);
|
||||
addRecentModel(model);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize with stored provider:', error);
|
||||
setFatalError(`Initialization failed: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('SETUP ERROR:', error);
|
||||
setFatalError(`Setup error: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the functions with better error handling
|
||||
detectStoredProvider();
|
||||
setupStoredProvider().catch((error) => {
|
||||
console.error('ASYNC SETUP ERROR:', error);
|
||||
setFatalError(`Async setup error: ${error.message || 'Unknown error'}`);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (fatalError) {
|
||||
return <ErrorUI error={new Error(fatalError)} />;
|
||||
}
|
||||
@@ -796,34 +520,18 @@ export default function App() {
|
||||
<div className="titlebar-drag-region" />
|
||||
<div>
|
||||
{view === 'loading' && <SuspenseLoader />}
|
||||
{view === 'welcome' &&
|
||||
(settingsV2Enabled ? (
|
||||
<ProviderSettings onClose={() => setView('chat')} isOnboarding={true} />
|
||||
) : (
|
||||
<WelcomeView
|
||||
onSubmit={() => {
|
||||
setView('chat');
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{view === 'settings' &&
|
||||
(settingsV2Enabled ? (
|
||||
<SettingsViewV2
|
||||
onClose={() => {
|
||||
setView('chat');
|
||||
}}
|
||||
setView={setView}
|
||||
viewOptions={viewOptions as SettingsViewOptions}
|
||||
/>
|
||||
) : (
|
||||
<SettingsView
|
||||
onClose={() => {
|
||||
setView('chat');
|
||||
}}
|
||||
setView={setView}
|
||||
viewOptions={viewOptions as SettingsViewOptions}
|
||||
/>
|
||||
))}
|
||||
{view === 'welcome' && (
|
||||
<ProviderSettings onClose={() => setView('chat')} isOnboarding={true} />
|
||||
)}
|
||||
{view === 'settings' && (
|
||||
<SettingsViewV2
|
||||
onClose={() => {
|
||||
setView('chat');
|
||||
}}
|
||||
setView={setView}
|
||||
viewOptions={viewOptions as SettingsViewOptions}
|
||||
/>
|
||||
)}
|
||||
{view === 'moreModels' && (
|
||||
<MoreModelsView
|
||||
onClose={() => {
|
||||
|
||||
Reference in New Issue
Block a user