@jupyterlite/ai 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/lib/chat-handler.d.ts +10 -4
  2. package/lib/chat-handler.js +42 -10
  3. package/lib/completion-provider.d.ts +5 -18
  4. package/lib/completion-provider.js +8 -34
  5. package/lib/{llm-models/anthropic-completer.d.ts → default-providers/Anthropic/completer.d.ts} +1 -1
  6. package/lib/{llm-models/anthropic-completer.js → default-providers/Anthropic/completer.js} +1 -1
  7. package/lib/{llm-models/chrome-completer.d.ts → default-providers/ChromeAI/completer.d.ts} +1 -1
  8. package/lib/{llm-models/chrome-completer.js → default-providers/ChromeAI/completer.js} +1 -1
  9. package/lib/default-providers/ChromeAI/instructions.d.ts +2 -0
  10. package/lib/default-providers/ChromeAI/instructions.js +24 -0
  11. package/lib/{llm-models/codestral-completer.d.ts → default-providers/MistralAI/completer.d.ts} +1 -1
  12. package/lib/{llm-models/codestral-completer.js → default-providers/MistralAI/completer.js} +1 -1
  13. package/lib/default-providers/MistralAI/instructions.d.ts +2 -0
  14. package/lib/default-providers/MistralAI/instructions.js +16 -0
  15. package/lib/{llm-models/openai-completer.d.ts → default-providers/OpenAI/completer.d.ts} +1 -1
  16. package/lib/{llm-models/openai-completer.js → default-providers/OpenAI/completer.js} +1 -1
  17. package/lib/default-providers/index.d.ts +2 -0
  18. package/lib/default-providers/index.js +60 -0
  19. package/lib/index.d.ts +3 -3
  20. package/lib/index.js +51 -64
  21. package/lib/provider.d.ts +45 -17
  22. package/lib/provider.js +97 -41
  23. package/lib/settings/base.json +7 -0
  24. package/lib/settings/panel.d.ts +84 -0
  25. package/lib/settings/panel.js +267 -0
  26. package/lib/tokens.d.ts +103 -0
  27. package/lib/tokens.js +5 -0
  28. package/package.json +12 -5
  29. package/schema/provider-registry.json +23 -0
  30. package/src/chat-handler.ts +50 -13
  31. package/src/completion-provider.ts +13 -37
  32. package/src/{llm-models/anthropic-completer.ts → default-providers/Anthropic/completer.ts} +2 -2
  33. package/src/{llm-models/chrome-completer.ts → default-providers/ChromeAI/completer.ts} +3 -2
  34. package/src/default-providers/ChromeAI/instructions.ts +24 -0
  35. package/src/{llm-models/codestral-completer.ts → default-providers/MistralAI/completer.ts} +2 -2
  36. package/src/default-providers/MistralAI/instructions.ts +16 -0
  37. package/src/{llm-models/openai-completer.ts → default-providers/OpenAI/completer.ts} +2 -2
  38. package/src/default-providers/index.ts +71 -0
  39. package/src/index.ts +75 -77
  40. package/src/provider.ts +100 -43
  41. package/src/settings/panel.tsx +346 -0
  42. package/src/tokens.ts +112 -0
  43. package/style/base.css +4 -0
  44. package/lib/llm-models/index.d.ts +0 -3
  45. package/lib/llm-models/index.js +0 -3
  46. package/lib/llm-models/utils.d.ts +0 -16
  47. package/lib/llm-models/utils.js +0 -86
  48. package/lib/slash-commands.d.ts +0 -16
  49. package/lib/slash-commands.js +0 -25
  50. package/lib/token.d.ts +0 -13
  51. package/lib/token.js +0 -2
  52. package/schema/ai-provider.json +0 -17
  53. package/src/llm-models/index.ts +0 -3
  54. package/src/llm-models/utils.ts +0 -90
  55. package/src/slash-commands.tsx +0 -55
  56. package/src/token.ts +0 -19
  57. /package/lib/{llm-models/base-completer.d.ts → base-completer.d.ts} +0 -0
  58. /package/lib/{llm-models/base-completer.js → base-completer.js} +0 -0
  59. /package/lib/{_provider-settings/anthropic.json → default-providers/Anthropic/settings-schema.json} +0 -0
  60. /package/lib/{_provider-settings/chromeAI.json → default-providers/ChromeAI/settings-schema.json} +0 -0
  61. /package/lib/{_provider-settings/mistralAI.json → default-providers/MistralAI/settings-schema.json} +0 -0
  62. /package/lib/{_provider-settings/openAI.json → default-providers/OpenAI/settings-schema.json} +0 -0
  63. /package/src/{llm-models/base-completer.ts → base-completer.ts} +0 -0
  64. /package/src/{llm-models/svg.d.ts → global.d.ts} +0 -0
package/src/index.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  import {
2
2
  ActiveCellManager,
3
- AutocompletionRegistry,
4
3
  buildChatSidebar,
5
4
  buildErrorWidget,
5
+ ChatCommandRegistry,
6
6
  IActiveCellManager,
7
- IAutocompletionCommandsProps,
8
- IAutocompletionRegistry
7
+ IChatCommandRegistry
9
8
  } from '@jupyter/chat';
10
9
  import {
11
10
  JupyterFrontEnd,
@@ -16,51 +15,40 @@ import { ICompletionProviderManager } from '@jupyterlab/completer';
16
15
  import { INotebookTracker } from '@jupyterlab/notebook';
17
16
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
18
17
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
18
+ import { IFormRendererRegistry } from '@jupyterlab/ui-components';
19
+ import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
20
+ import { ISecretsManager } from 'jupyter-secrets-manager';
19
21
 
20
22
  import { ChatHandler } from './chat-handler';
21
- import { getSettings } from './llm-models';
22
- import { AIProvider } from './provider';
23
- import { renderSlashCommandOption } from './slash-commands';
24
- import { IAIProvider } from './token';
25
-
26
- const autocompletionRegistryPlugin: JupyterFrontEndPlugin<IAutocompletionRegistry> =
27
- {
28
- id: '@jupyterlite/ai:autocompletion-registry',
29
- description: 'Autocompletion registry',
30
- autoStart: true,
31
- provides: IAutocompletionRegistry,
32
- activate: () => {
33
- const autocompletionRegistry = new AutocompletionRegistry();
34
- const options = ['/clear'];
35
- const autocompletionCommands: IAutocompletionCommandsProps = {
36
- opener: '/',
37
- commands: options.map(option => {
38
- return {
39
- id: option.slice(1),
40
- label: option,
41
- description: 'Clear the chat window'
42
- };
43
- }),
44
- props: {
45
- renderOption: renderSlashCommandOption
46
- }
47
- };
48
- autocompletionRegistry.add('jupyterlite-ai', autocompletionCommands);
49
- return autocompletionRegistry;
50
- }
51
- };
23
+ import { CompletionProvider } from './completion-provider';
24
+ import { defaultProviderPlugins } from './default-providers';
25
+ import { AIProviderRegistry } from './provider';
26
+ import { aiSettingsRenderer } from './settings/panel';
27
+ import { IAIProviderRegistry } from './tokens';
28
+
29
+ const chatCommandRegistryPlugin: JupyterFrontEndPlugin<IChatCommandRegistry> = {
30
+ id: '@jupyterlite/ai:autocompletion-registry',
31
+ description: 'Autocompletion registry',
32
+ autoStart: true,
33
+ provides: IChatCommandRegistry,
34
+ activate: () => {
35
+ const registry = new ChatCommandRegistry();
36
+ registry.addProvider(new ChatHandler.ClearCommandProvider());
37
+ return registry;
38
+ }
39
+ };
52
40
 
53
41
  const chatPlugin: JupyterFrontEndPlugin<void> = {
54
42
  id: '@jupyterlite/ai:chat',
55
43
  description: 'LLM chat extension',
56
44
  autoStart: true,
57
- requires: [IAIProvider, IRenderMimeRegistry, IAutocompletionRegistry],
45
+ requires: [IAIProviderRegistry, IRenderMimeRegistry, IChatCommandRegistry],
58
46
  optional: [INotebookTracker, ISettingRegistry, IThemeManager],
59
47
  activate: async (
60
48
  app: JupyterFrontEnd,
61
- aiProvider: IAIProvider,
49
+ providerRegistry: IAIProviderRegistry,
62
50
  rmRegistry: IRenderMimeRegistry,
63
- autocompletionRegistry: IAutocompletionRegistry,
51
+ chatCommandRegistry: IChatCommandRegistry,
64
52
  notebookTracker: INotebookTracker | null,
65
53
  settingsRegistry: ISettingRegistry | null,
66
54
  themeManager: IThemeManager | null
@@ -74,8 +62,8 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
74
62
  }
75
63
 
76
64
  const chatHandler = new ChatHandler({
77
- aiProvider: aiProvider,
78
- activeCellManager: activeCellManager
65
+ providerRegistry,
66
+ activeCellManager
79
67
  });
80
68
 
81
69
  let sendWithShiftEnter = false;
@@ -116,7 +104,7 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
116
104
  model: chatHandler,
117
105
  themeManager,
118
106
  rmRegistry,
119
- autocompletionRegistry
107
+ chatCommandRegistry
120
108
  });
121
109
  chatWidget.title.caption = 'Jupyterlite AI Chat';
122
110
  } catch (e) {
@@ -129,50 +117,54 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
129
117
  }
130
118
  };
131
119
 
132
- const aiProviderPlugin: JupyterFrontEndPlugin<IAIProvider> = {
133
- id: '@jupyterlite/ai:ai-provider',
120
+ const completerPlugin: JupyterFrontEndPlugin<void> = {
121
+ id: '@jupyterlite/ai:completer',
134
122
  autoStart: true,
135
- requires: [ICompletionProviderManager, ISettingRegistry],
136
- provides: IAIProvider,
123
+ requires: [IAIProviderRegistry, ICompletionProviderManager],
137
124
  activate: (
138
125
  app: JupyterFrontEnd,
139
- manager: ICompletionProviderManager,
140
- settingRegistry: ISettingRegistry
141
- ): IAIProvider => {
142
- const aiProvider = new AIProvider({
143
- completionProviderManager: manager,
126
+ providerRegistry: IAIProviderRegistry,
127
+ manager: ICompletionProviderManager
128
+ ): void => {
129
+ const completer = new CompletionProvider({
130
+ providerRegistry,
144
131
  requestCompletion: () => app.commands.execute('inline-completer:invoke')
145
132
  });
133
+ manager.registerInlineProvider(completer);
134
+ }
135
+ };
146
136
 
147
- let currentProvider = 'None';
137
+ const providerRegistryPlugin: JupyterFrontEndPlugin<IAIProviderRegistry> = {
138
+ id: '@jupyterlite/ai:provider-registry',
139
+ autoStart: true,
140
+ requires: [IFormRendererRegistry, ISettingRegistry],
141
+ optional: [IRenderMimeRegistry, ISecretsManager],
142
+ provides: IAIProviderRegistry,
143
+ activate: (
144
+ app: JupyterFrontEnd,
145
+ editorRegistry: IFormRendererRegistry,
146
+ settingRegistry: ISettingRegistry,
147
+ rmRegistry?: IRenderMimeRegistry,
148
+ secretsManager?: ISecretsManager
149
+ ): IAIProviderRegistry => {
150
+ const providerRegistry = new AIProviderRegistry();
151
+
152
+ editorRegistry.addRenderer(
153
+ '@jupyterlite/ai:provider-registry.AIprovider',
154
+ aiSettingsRenderer({ providerRegistry, rmRegistry, secretsManager })
155
+ );
148
156
  settingRegistry
149
- .load(aiProviderPlugin.id)
157
+ .load(providerRegistryPlugin.id)
150
158
  .then(settings => {
151
159
  const updateProvider = () => {
152
- const provider = settings.get('provider').composite as string;
153
- if (provider !== currentProvider) {
154
- // Update the settings panel.
155
- currentProvider = provider;
156
- const settingsProperties = settings.schema.properties;
157
- if (settingsProperties) {
158
- const schemaKeys = Object.keys(settingsProperties);
159
- schemaKeys.forEach(key => {
160
- if (key !== 'provider') {
161
- delete settings.schema.properties?.[key];
162
- }
163
- });
164
- const properties = getSettings(provider);
165
- if (properties === null) {
166
- return;
167
- }
168
- Object.entries(properties).forEach(([name, value], index) => {
169
- settingsProperties[name] = value as ISettingRegistry.IProperty;
170
- });
171
- }
172
- }
173
-
174
160
  // Update the settings to the AI providers.
175
- aiProvider.setModels(provider, settings.composite);
161
+ const providerSettings = (settings.get('AIprovider').composite ?? {
162
+ provider: 'None'
163
+ }) as ReadonlyPartialJSONObject;
164
+ providerRegistry.setProvider(
165
+ providerSettings.provider as string,
166
+ providerSettings
167
+ );
176
168
  };
177
169
 
178
170
  settings.changed.connect(() => updateProvider());
@@ -180,13 +172,19 @@ const aiProviderPlugin: JupyterFrontEndPlugin<IAIProvider> = {
180
172
  })
181
173
  .catch(reason => {
182
174
  console.error(
183
- `Failed to load settings for ${aiProviderPlugin.id}`,
175
+ `Failed to load settings for ${providerRegistryPlugin.id}`,
184
176
  reason
185
177
  );
186
178
  });
187
179
 
188
- return aiProvider;
180
+ return providerRegistry;
189
181
  }
190
182
  };
191
183
 
192
- export default [chatPlugin, autocompletionRegistryPlugin, aiProviderPlugin];
184
+ export default [
185
+ providerRegistryPlugin,
186
+ chatCommandRegistryPlugin,
187
+ chatPlugin,
188
+ completerPlugin,
189
+ ...defaultProviderPlugins
190
+ ];
package/src/provider.ts CHANGED
@@ -4,11 +4,13 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models';
4
4
  import { ISignal, Signal } from '@lumino/signaling';
5
5
  import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
6
6
 
7
- import { CompletionProvider } from './completion-provider';
8
- import { getChatModel, IBaseCompleter } from './llm-models';
9
- import { IAIProvider } from './token';
7
+ import { IBaseCompleter } from './base-completer';
8
+ import { IAIProvider, IAIProviderRegistry } from './tokens';
9
+ import { JSONSchema7 } from 'json-schema';
10
10
 
11
- export const chatSystemPrompt = (options: AIProvider.IPromptOptions) => `
11
+ export const chatSystemPrompt = (
12
+ options: AIProviderRegistry.IPromptOptions
13
+ ) => `
12
14
  You are Jupyternaut, a conversational assistant living in JupyterLab to help users.
13
15
  You are not a language model, but rather an application built on a foundation model from ${options.provider_name}.
14
16
  You are talkative and you provide lots of specific details from the foundation model's context.
@@ -36,40 +38,79 @@ would write.
36
38
  Do not include the prompt in the output, only the string that should be appended to the current input.
37
39
  `;
38
40
 
39
- export class AIProvider implements IAIProvider {
40
- constructor(options: AIProvider.IOptions) {
41
- this._completionProvider = new CompletionProvider({
42
- name: 'None',
43
- settings: {},
44
- requestCompletion: options.requestCompletion
45
- });
46
- options.completionProviderManager.registerInlineProvider(
47
- this._completionProvider
48
- );
41
+ export class AIProviderRegistry implements IAIProviderRegistry {
42
+ /**
43
+ * Get the list of provider names.
44
+ */
45
+ get providers(): string[] {
46
+ return Array.from(this._providers.keys());
47
+ }
48
+
49
+ /**
50
+ * Add a new provider.
51
+ */
52
+ add(provider: IAIProvider): void {
53
+ if (this._providers.has(provider.name)) {
54
+ throw new Error(
55
+ `A AI provider named '${provider.name}' is already registered`
56
+ );
57
+ }
58
+ this._providers.set(provider.name, provider);
49
59
  }
50
60
 
51
- get name(): string {
61
+ /**
62
+ * Get the current provider name.
63
+ */
64
+ get currentName(): string {
52
65
  return this._name;
53
66
  }
54
67
 
55
68
  /**
56
69
  * Get the current completer of the completion provider.
57
70
  */
58
- get completer(): IBaseCompleter | null {
59
- if (this._name === null) {
71
+ get currentCompleter(): IBaseCompleter | null {
72
+ if (this._name === 'None') {
60
73
  return null;
61
74
  }
62
- return this._completionProvider.completer;
75
+ return this._completer;
63
76
  }
64
77
 
65
78
  /**
66
79
  * Get the current llm chat model.
67
80
  */
68
- get chatModel(): BaseChatModel | null {
69
- if (this._name === null) {
81
+ get currentChatModel(): BaseChatModel | null {
82
+ if (this._name === 'None') {
70
83
  return null;
71
84
  }
72
- return this._llmChatModel;
85
+ return this._chatModel;
86
+ }
87
+
88
+ /**
89
+ * Get the settings schema of a given provider.
90
+ */
91
+ getSettingsSchema(provider: string): JSONSchema7 {
92
+ return (this._providers.get(provider)?.settingsSchema?.properties ||
93
+ {}) as JSONSchema7;
94
+ }
95
+
96
+ /**
97
+ * Get the instructions of a given provider.
98
+ */
99
+ getInstructions(provider: string): string | undefined {
100
+ return this._providers.get(provider)?.instructions;
101
+ }
102
+
103
+ /**
104
+ * Format an error message from the current provider.
105
+ */
106
+ formatErrorMessage(error: any): string {
107
+ if (this._currentProvider?.errorMessage) {
108
+ return this._currentProvider?.errorMessage(error);
109
+ }
110
+ if (error.message) {
111
+ return error.message;
112
+ }
113
+ return error;
73
114
  }
74
115
 
75
116
  /**
@@ -87,43 +128,59 @@ export class AIProvider implements IAIProvider {
87
128
  }
88
129
 
89
130
  /**
90
- * Set the models (chat model and completer).
91
- * Creates the models if the name has changed, otherwise only updates their config.
131
+ * Set the providers (chat model and completer).
132
+ * Creates the providers if the name has changed, otherwise only updates their config.
92
133
  *
93
- * @param name - the name of the model to use.
134
+ * @param name - the name of the provider to use.
94
135
  * @param settings - the settings for the models.
95
136
  */
96
- setModels(name: string, settings: ReadonlyPartialJSONObject) {
97
- try {
98
- this._completionProvider.setCompleter(name, settings);
99
- this._completerError = '';
100
- } catch (e: any) {
101
- this._completerError = e.message;
137
+ setProvider(name: string, settings: ReadonlyPartialJSONObject): void {
138
+ this._currentProvider = this._providers.get(name) ?? null;
139
+
140
+ if (this._currentProvider?.completer !== undefined) {
141
+ try {
142
+ this._completer = new this._currentProvider.completer({ ...settings });
143
+ this._completerError = '';
144
+ } catch (e: any) {
145
+ this._completerError = e.message;
146
+ }
147
+ } else {
148
+ this._completer = null;
102
149
  }
103
- try {
104
- this._llmChatModel = getChatModel(name, settings);
105
- this._chatError = '';
106
- } catch (e: any) {
107
- this._chatError = e.message;
108
- this._llmChatModel = null;
150
+
151
+ if (this._currentProvider?.chatModel !== undefined) {
152
+ try {
153
+ this._chatModel = new this._currentProvider.chatModel({ ...settings });
154
+ this._chatError = '';
155
+ } catch (e: any) {
156
+ this._chatError = e.message;
157
+ this._chatModel = null;
158
+ }
159
+ } else {
160
+ this._chatModel = null;
109
161
  }
110
162
  this._name = name;
111
- this._modelChange.emit();
163
+ this._providerChanged.emit();
112
164
  }
113
165
 
114
- get modelChange(): ISignal<IAIProvider, void> {
115
- return this._modelChange;
166
+ /**
167
+ * A signal emitting when the provider or its settings has changed.
168
+ */
169
+ get providerChanged(): ISignal<IAIProviderRegistry, void> {
170
+ return this._providerChanged;
116
171
  }
117
172
 
118
- private _completionProvider: CompletionProvider;
119
- private _llmChatModel: BaseChatModel | null = null;
173
+ private _currentProvider: IAIProvider | null = null;
174
+ private _completer: IBaseCompleter | null = null;
175
+ private _chatModel: BaseChatModel | null = null;
120
176
  private _name: string = 'None';
121
- private _modelChange = new Signal<IAIProvider, void>(this);
177
+ private _providerChanged = new Signal<IAIProviderRegistry, void>(this);
122
178
  private _chatError: string = '';
123
179
  private _completerError: string = '';
180
+ private _providers = new Map<string, IAIProvider>();
124
181
  }
125
182
 
126
- export namespace AIProvider {
183
+ export namespace AIProviderRegistry {
127
184
  /**
128
185
  * The options for the LLM provider.
129
186
  */