diff --git a/packages/jupyter-ai/jupyter_ai/config_manager.py b/packages/jupyter-ai/jupyter_ai/config_manager.py index 5adc604cf..5e35fdde1 100644 --- a/packages/jupyter-ai/jupyter_ai/config_manager.py +++ b/packages/jupyter-ai/jupyter_ai/config_manager.py @@ -59,6 +59,12 @@ class BlockedModelError(Exception): pass +class ConfigValidationError(Exception): + def __init__(self, property_name, message): + self.property_name = property_name + super().__init__(message) + + def _validate_provider_authn(config: GlobalConfig, provider: AnyProvider): # TODO: handle non-env auth strategies if not provider.auth_strategy or provider.auth_strategy.type != "env": @@ -171,7 +177,14 @@ def _init_config(self): # re-write to the file to validate the config and apply any # updates to the config file immediately - self._write_config(config) + try: + self._write_config(config) + except ConfigValidationError as e: + if e.property_name == "model_provider_id": + self.log.warning(f"{str(e)} Setting to None") + config.model_provider_id = None + self._write_config(config) + return properties = self.validator.schema.get("properties", {}) @@ -213,8 +226,9 @@ def _validate_config(self, config: GlobalConfig): # verify model is declared by some provider if not lm_provider: - raise ValueError( - f"No language model is associated with '{config.model_provider_id}'." + raise ConfigValidationError( + "model_provider_id", + f"No language model is associated with '{config.model_provider_id}'.", ) # verify model is not blocked @@ -330,6 +344,23 @@ def update_config(self, config_update: UpdateConfigRequest): Merger.merge(config_dict, config_update.dict(exclude_unset=True)) self._write_config(GlobalConfig(**config_dict)) + def delete_config(self): + try: + if os.path.exists(self.config_path): + os.remove(self.config_path) + self.log.info(f"Configutation file {self.config_path} has been deleted") + self._config = None + self._last_read = None + else: + self.log.warning( + f"Configuration file {self.config_path} does not exist" + ) + except Exception as e: + self.log.warning( + f"Failed to delete configuration file {self.config_path}: {e}" + ) + raise + # this cannot be a property, as the parent Configurable already defines the # self.config attr. def get_config(self): diff --git a/packages/jupyter-ai/jupyter_ai/handlers.py b/packages/jupyter-ai/jupyter_ai/handlers.py index 80f094f1f..2b3556868 100644 --- a/packages/jupyter-ai/jupyter_ai/handlers.py +++ b/packages/jupyter-ai/jupyter_ai/handlers.py @@ -382,6 +382,15 @@ def post(self): 500, "Unexpected error occurred while updating the config." ) from e + @web.authenticated + def delete(self): + try: + self.config_manager.delete_config() + self.set_status(204) + self.finish() + except Exception as e: + raise HTTPError(500, str(e)) + class ApiKeysHandler(BaseAPIHandler): @property diff --git a/packages/jupyter-ai/src/handler.ts b/packages/jupyter-ai/src/handler.ts index ec91c1f3f..d99c77eb6 100644 --- a/packages/jupyter-ai/src/handler.ts +++ b/packages/jupyter-ai/src/handler.ts @@ -191,6 +191,10 @@ export namespace AiService { }); } + export async function deleteConfig(): Promise { + return requestAPI('config', { method: 'DELETE' }); + } + export async function deleteApiKey(keyName: string): Promise { return requestAPI(`api_keys/${keyName}`, { method: 'DELETE' diff --git a/packages/jupyter-ai/src/index.ts b/packages/jupyter-ai/src/index.ts index e48e2b211..345c01a84 100644 --- a/packages/jupyter-ai/src/index.ts +++ b/packages/jupyter-ai/src/index.ts @@ -12,6 +12,7 @@ import { buildChatSidebar } from './widgets/chat-sidebar'; import { SelectionWatcher } from './selection-watcher'; import { ChatHandler } from './chat_handler'; import { buildErrorWidget } from './widgets/chat-error'; +import { AiService } from './handler'; export type DocumentTracker = IWidgetTracker; @@ -46,7 +47,7 @@ const plugin: JupyterFrontEndPlugin = { globalAwareness ); } catch (e) { - chatWidget = buildErrorWidget(); + chatWidget = buildErrorWidget(AiService.deleteConfig); } /** diff --git a/packages/jupyter-ai/src/widgets/chat-error.tsx b/packages/jupyter-ai/src/widgets/chat-error.tsx index 3b8f8ef95..24abccc5e 100644 --- a/packages/jupyter-ai/src/widgets/chat-error.tsx +++ b/packages/jupyter-ai/src/widgets/chat-error.tsx @@ -2,10 +2,12 @@ import React from 'react'; import { ReactWidget } from '@jupyterlab/apputils'; import { chatIcon } from '../icons'; -import { Alert, Box } from '@mui/material'; +import { Alert, Box, Button } from '@mui/material'; import { JlThemeProvider } from '../components/jl-theme-provider'; -export function buildErrorWidget(): ReactWidget { +export function buildErrorWidget( + deleteConfig: () => Promise +): ReactWidget { const ErrorWidget = ReactWidget.create( There seems to be a problem with the Chat backend, please look at the JupyterLab server logs or contact your administrator to correct - this problem. + this problem. Deleting the config file + `$JUPYTER_DATA_DIR/jupyter_ai/config.json` and then restarting + `jupyter lab` may fix the issue if it is a result of the config + changes. + + +