Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to delete config to buildErrorWidget #441

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions packages/jupyter-ai/jupyter_ai/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -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", {})
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
9 changes: 9 additions & 0 deletions packages/jupyter-ai/jupyter_ai/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions packages/jupyter-ai/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ export namespace AiService {
});
}

export async function deleteConfig(): Promise<void> {
return requestAPI<void>('config', { method: 'DELETE' });
}

export async function deleteApiKey(keyName: string): Promise<void> {
return requestAPI<void>(`api_keys/${keyName}`, {
method: 'DELETE'
Expand Down
3 changes: 2 additions & 1 deletion packages/jupyter-ai/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<IDocumentWidget>;

Expand Down Expand Up @@ -46,7 +47,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
globalAwareness
);
} catch (e) {
chatWidget = buildErrorWidget();
chatWidget = buildErrorWidget(AiService.deleteConfig);
}

/**
Expand Down
16 changes: 13 additions & 3 deletions packages/jupyter-ai/src/widgets/chat-error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>
): ReactWidget {
const ErrorWidget = ReactWidget.create(
<JlThemeProvider>
<Box
Expand All @@ -22,8 +24,16 @@ export function buildErrorWidget(): ReactWidget {
<Alert severity="error">
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.
</Alert>
<Box sx={{ marginTop: 2 }}>
<Button variant="contained" onClick={deleteConfig}>
Delete Config File
</Button>
</Box>
</Box>
</Box>
</JlThemeProvider>
Expand Down
Loading