Skip to content

Commit

Permalink
WIP: fix Jupyter AI completion settings
Browse files Browse the repository at this point in the history
  • Loading branch information
dlqqq committed Jan 17, 2024
1 parent a8dd79c commit cfa8144
Show file tree
Hide file tree
Showing 6 changed files with 378 additions and 428 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"test": "lerna run test"
},
"devDependencies": {
"@jupyterlab/builder": "^4",
"lerna": "^6.4.1",
"nx": "^15.9.2"
},
Expand Down
25 changes: 14 additions & 11 deletions packages/jupyter-ai/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,19 @@
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@jupyter/collaboration": "^1",
"@jupyterlab/application": "^4",
"@jupyterlab/cells": "^4",
"@jupyterlab/codeeditor": "^4",
"@jupyterlab/codemirror": "^4",
"@jupyterlab/application": "^4.1.0-beta.0",
"@jupyterlab/apputils": "^4.2.0-beta.0",
"@jupyterlab/cells": "^4.1.0-beta.0",
"@jupyterlab/codeeditor": "^4.1.0-beta.0",
"@jupyterlab/codemirror": "^4.1.0-beta.0",
"@jupyterlab/completer": "^4.1.0-beta.0",
"@jupyterlab/coreutils": "^6",
"@jupyterlab/fileeditor": "^4",
"@jupyterlab/notebook": "^4",
"@jupyterlab/services": "^7",
"@jupyterlab/ui-components": "^4",
"@jupyterlab/coreutils": "^6.1.0-beta.0",
"@jupyterlab/docregistry": "^4.1.0-beta.0",
"@jupyterlab/fileeditor": "^4.1.0-beta.0",
"@jupyterlab/notebook": "^4.1.0-beta.0",
"@jupyterlab/services": "^7.1.0-beta.0",
"@jupyterlab/settingregistry": "^4.1.0-beta.0",
"@jupyterlab/ui-components": "^4.1.0-beta.0",
"@mui/icons-material": "^5.11.0",
"@mui/material": "^5.11.0",
"react": "^18.2.0",
Expand All @@ -84,8 +87,8 @@
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@jupyterlab/builder": "^4",
"@jupyterlab/testutils": "^4",
"@jupyterlab/builder": "^4.1.0-beta.0",
"@jupyterlab/testutils": "^4.1.0-beta.0",
"@types/jest": "^29",
"@types/react-syntax-highlighter": "^15.5.6",
"@typescript-eslint/eslint-plugin": "^4.8.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/jupyter-ai/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["hatchling>=1.4.0", "jupyterlab~=4.0", "hatch-nodejs-version"]
requires = ["hatchling>=1.4.0", "jupyterlab==4.1.0b0", "hatch-nodejs-version"]
build-backend = "hatchling.build"

[project]
Expand Down
108 changes: 75 additions & 33 deletions packages/jupyter-ai/src/completions/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ export namespace CommandIDs {
const INLINE_COMPLETER_PLUGIN =
'@jupyterlab/completer-extension:inline-completer';

/**
* Type of the settings object for the inline completer plugin.
*/
type IcPluginSettings = ISettingRegistry.ISettings & {
user: {
providers: {
[key: string]: unknown;
[JaiInlineProvider.ID]?: JaiInlineProvider.ISettings;
};
};
};

console.log('is anything gonna happen?');

export const completionPlugin: JupyterFrontEndPlugin<void> = {
id: 'jupyter_ai:inline-completions',
autoStart: true,
Expand All @@ -44,20 +58,24 @@ export const completionPlugin: JupyterFrontEndPlugin<void> = {
settingRegistry: ISettingRegistry,
statusItem: IJaiStatusItem | null
): Promise<void> => {
console.log('HELLO???');
if (typeof completionManager.registerInlineProvider === 'undefined') {
// Gracefully short-circuit on JupyterLab 4.0 and Notebook 7.0
console.warn(
'Inline completions are only supported in JupyterLab 4.1+ and Jupyter Notebook 7.1+'
);
return;
}
console.log('1');
const completionHandler = new CompletionWebsocketHandler();
const provider = new JaiInlineProvider({
completionHandler,
languageRegistry
});
console.log('2');
await completionHandler.initialize();
completionManager.registerInlineProvider(provider);
console.log('3');

const findCurrentLanguage = (): IEditorLanguage | null => {
const widget = app.shell.currentWidget;
Expand All @@ -68,28 +86,58 @@ export const completionPlugin: JupyterFrontEndPlugin<void> = {
return languageRegistry.findByMIME(editor.model.mimeType);
};

let settings: ISettingRegistry.ISettings | null = null;
console.log('PRE LOAD');
// ic := inline completion
let icSettings = (await settingRegistry.load(
INLINE_COMPLETER_PLUGIN
)) as IcPluginSettings;
console.log('LOADED');

// icp := inline completion providers
let icpSettings = icSettings.user.providers;

// jaiIcp := Jupyter AI inline completion provider
// if not defined, the default settings are used
let jaiIcpSettings =
icpSettings[JaiInlineProvider.ID] || JaiInlineProvider.DEFAULT_SETTINGS;

// make sure the object references are updated when the underlying settings
// are updated. admittedly duplicates the previous 3 variable definitions.
settingRegistry.pluginChanged.connect(async (_emitter, plugin) => {
if (plugin === INLINE_COMPLETER_PLUGIN) {
// Only load the settings once the plugin settings were transformed
settings = await settingRegistry.load(INLINE_COMPLETER_PLUGIN);
icSettings = (await settingRegistry.load(
INLINE_COMPLETER_PLUGIN
)) as IcPluginSettings;
icpSettings = icSettings.user.providers;
jaiIcpSettings =
icpSettings[JaiInlineProvider.ID] ||
JaiInlineProvider.DEFAULT_SETTINGS;
}
});

/**
* Updates only the Jupyter AI inline completion provider (JaiIcp) settings.
* If the JaiIcp settings are undefined prior to this call, the new settings
* object is merged with the default JaiIcp settings defined in
* `JaiInlineProvider.DEFAULT_SETTINGS`.
*/
function updateJaiIcpSettings(
newJaiIcpSettings: Partial<JaiInlineProvider.ISettings>
) {
const newProviders = {
...icpSettings,
[JaiInlineProvider.ID]: {
...jaiIcpSettings,
...newJaiIcpSettings
}
};

icSettings.set('providers', newProviders);
}

app.commands.addCommand(CommandIDs.toggleCompletions, {
execute: () => {
if (!settings) {
return;
}
const providers = Object.assign({}, settings.user.providers) as any;
const ourSettings = {
...JaiInlineProvider.DEFAULT_SETTINGS,
...providers[provider.identifier]
};
const wasEnabled = ourSettings['enabled'];
providers[provider.identifier]['enabled'] = !wasEnabled;
settings.set('providers', providers);
updateJaiIcpSettings({ enabled: !jaiIcpSettings.enabled });
},
label: 'Enable Jupyternaut Completions',
isToggled: () => {
Expand All @@ -100,38 +148,32 @@ export const completionPlugin: JupyterFrontEndPlugin<void> = {
app.commands.addCommand(CommandIDs.toggleLanguageCompletions, {
execute: () => {
const language = findCurrentLanguage();
if (!settings || !language) {
if (!language) {
return;
}
const providers = Object.assign({}, settings.user.providers) as any;
const ourSettings: JaiInlineProvider.ISettings = {
...JaiInlineProvider.DEFAULT_SETTINGS,
...providers[provider.identifier]
};
const wasDisabled = ourSettings['disabledLanguages'].includes(
language.name
);
const disabledList: string[] =
providers[provider.identifier]['disabledLanguages'];
if (wasDisabled) {
disabledList.filter(name => name !== language.name);
} else {
disabledList.push(language.name);
}
settings.set('providers', providers);

const disabledLanguages = [...jaiIcpSettings.disabledLanguages];
const newDisabledLanguages = disabledLanguages.includes(language.name)
? disabledLanguages.filter(l => l !== language.name)
: disabledLanguages.concat(language.name);

updateJaiIcpSettings({
disabledLanguages: newDisabledLanguages
});
},
label: () => {
const language = findCurrentLanguage();
return language
? `Enable Completions in ${displayName(language)}`
: 'Enable Completions for Language of Current Editor';
: 'Enable Completions for <language>';
},
isToggled: () => {
const language = findCurrentLanguage();
return !!language && provider.isLanguageEnabled(language.name);
},
isEnabled: () => {
return !!findCurrentLanguage() && provider.isEnabled();
const language = findCurrentLanguage();
return !!language && provider.isEnabled();
}
});

Expand Down
4 changes: 3 additions & 1 deletion packages/jupyter-ai/src/completions/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function displayName(language: IEditorLanguage): string {
}

export class JaiInlineProvider implements IInlineCompletionProvider {
readonly identifier = '@jupyterlab/jupyter-ai';
readonly identifier = JaiInlineProvider.ID;
readonly icon = jupyternautIcon.bindprops({ width: 16, top: 1 });

constructor(protected options: JaiInlineProvider.IOptions) {
Expand Down Expand Up @@ -257,6 +257,8 @@ export class JaiInlineProvider implements IInlineCompletionProvider {
}

export namespace JaiInlineProvider {
export const ID = '@jupyterlab/jupyter-ai';

export interface IOptions {
completionHandler: CompletionWebsocketHandler;
languageRegistry: IEditorLanguageRegistry;
Expand Down
Loading

0 comments on commit cfa8144

Please sign in to comment.