@jupyterlite/ai 0.6.2 → 0.7.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 (65) hide show
  1. package/README.md +1 -1
  2. package/lib/base-completer.d.ts +0 -5
  3. package/lib/chat-handler.d.ts +19 -5
  4. package/lib/chat-handler.js +47 -26
  5. package/lib/completion-provider.d.ts +2 -2
  6. package/lib/completion-provider.js +4 -3
  7. package/lib/components/stop-button.d.ts +0 -1
  8. package/lib/default-providers/Anthropic/completer.d.ts +0 -2
  9. package/lib/default-providers/Anthropic/completer.js +2 -4
  10. package/lib/default-providers/ChromeAI/completer.d.ts +0 -2
  11. package/lib/default-providers/ChromeAI/completer.js +2 -4
  12. package/lib/default-providers/ChromeAI/instructions.d.ts +4 -0
  13. package/lib/default-providers/ChromeAI/instructions.js +18 -0
  14. package/lib/default-providers/MistralAI/completer.d.ts +0 -2
  15. package/lib/default-providers/MistralAI/completer.js +3 -4
  16. package/lib/default-providers/Ollama/completer.d.ts +17 -0
  17. package/lib/default-providers/Ollama/completer.js +49 -0
  18. package/lib/default-providers/Ollama/instructions.d.ts +2 -0
  19. package/lib/default-providers/Ollama/instructions.js +70 -0
  20. package/lib/default-providers/Ollama/settings-schema.json +146 -0
  21. package/lib/default-providers/OpenAI/completer.d.ts +0 -2
  22. package/lib/default-providers/OpenAI/completer.js +2 -4
  23. package/lib/default-providers/WebLLM/completer.d.ts +27 -0
  24. package/lib/default-providers/WebLLM/completer.js +136 -0
  25. package/lib/default-providers/WebLLM/instructions.d.ts +6 -0
  26. package/lib/default-providers/WebLLM/instructions.js +32 -0
  27. package/lib/default-providers/WebLLM/settings-schema.json +21 -0
  28. package/lib/default-providers/index.js +119 -4
  29. package/lib/index.d.ts +2 -2
  30. package/lib/index.js +16 -26
  31. package/lib/provider.d.ts +11 -13
  32. package/lib/provider.js +120 -52
  33. package/lib/settings/index.d.ts +0 -1
  34. package/lib/settings/index.js +0 -1
  35. package/lib/settings/panel.d.ts +37 -8
  36. package/lib/settings/panel.js +225 -131
  37. package/lib/tokens.d.ts +21 -2
  38. package/lib/types/ai-model.d.ts +24 -0
  39. package/lib/types/ai-model.js +5 -0
  40. package/package.json +14 -11
  41. package/schema/provider-registry.json +0 -6
  42. package/src/base-completer.ts +0 -6
  43. package/src/chat-handler.ts +40 -7
  44. package/src/completion-provider.ts +2 -2
  45. package/src/default-providers/Anthropic/completer.ts +0 -5
  46. package/src/default-providers/ChromeAI/completer.ts +0 -5
  47. package/src/default-providers/ChromeAI/instructions.ts +21 -0
  48. package/src/default-providers/MistralAI/completer.ts +0 -5
  49. package/src/default-providers/Ollama/completer.ts +62 -0
  50. package/src/default-providers/Ollama/instructions.ts +70 -0
  51. package/src/default-providers/OpenAI/completer.ts +0 -5
  52. package/src/default-providers/WebLLM/completer.ts +162 -0
  53. package/src/default-providers/WebLLM/instructions.ts +33 -0
  54. package/src/default-providers/index.ts +151 -14
  55. package/src/index.ts +17 -29
  56. package/src/provider.ts +132 -45
  57. package/src/settings/index.ts +0 -1
  58. package/src/settings/panel.tsx +207 -73
  59. package/src/tokens.ts +23 -2
  60. package/src/types/ai-model.ts +37 -0
  61. package/src/types/service-worker.d.ts +6 -0
  62. package/style/base.css +5 -0
  63. package/lib/settings/settings-connector.d.ts +0 -31
  64. package/lib/settings/settings-connector.js +0 -61
  65. package/src/settings/settings-connector.ts +0 -88
@@ -2,28 +2,44 @@ import {
2
2
  JupyterFrontEnd,
3
3
  JupyterFrontEndPlugin
4
4
  } from '@jupyterlab/application';
5
+ import { Notification } from '@jupyterlab/apputils';
6
+
5
7
  import { ChatAnthropic } from '@langchain/anthropic';
8
+ import { ChatWebLLM } from '@langchain/community/chat_models/webllm';
6
9
  import { ChromeAI } from '@langchain/community/experimental/llms/chrome_ai';
7
10
  import { ChatMistralAI } from '@langchain/mistralai';
11
+ import { ChatOllama } from '@langchain/ollama';
8
12
  import { ChatOpenAI } from '@langchain/openai';
9
13
 
10
- import { IAIProvider, IAIProviderRegistry } from '../tokens';
11
-
12
14
  // Import completers
13
15
  import { AnthropicCompleter } from './Anthropic/completer';
14
16
  import { ChromeCompleter } from './ChromeAI/completer';
15
17
  import { CodestralCompleter } from './MistralAI/completer';
18
+ import { OllamaCompleter } from './Ollama/completer';
16
19
  import { OpenAICompleter } from './OpenAI/completer';
20
+ import { WebLLMCompleter } from './WebLLM/completer';
17
21
 
18
22
  // Import Settings
19
23
  import AnthropicSettings from './Anthropic/settings-schema.json';
20
24
  import ChromeAISettings from './ChromeAI/settings-schema.json';
21
25
  import MistralAISettings from './MistralAI/settings-schema.json';
26
+ import OllamaAISettings from './Ollama/settings-schema.json';
22
27
  import OpenAISettings from './OpenAI/settings-schema.json';
28
+ import WebLLMSettings from './WebLLM/settings-schema.json';
23
29
 
24
30
  // Import instructions
25
- import ChromeAIInstructions from './ChromeAI/instructions';
31
+ import ChromeAIInstructions, {
32
+ compatibilityCheck as chromeAICompatibilityCheck
33
+ } from './ChromeAI/instructions';
26
34
  import MistralAIInstructions from './MistralAI/instructions';
35
+ import OllamaInstructions from './Ollama/instructions';
36
+ import WebLLMInstructions, {
37
+ compatibilityCheck as webLLMCompatibilityCheck
38
+ } from './WebLLM/instructions';
39
+
40
+ import { prebuiltAppConfig } from '@mlc-ai/web-llm';
41
+
42
+ import { IAIProvider, IAIProviderRegistry } from '../tokens';
27
43
 
28
44
  // Build the AIProvider list
29
45
  const AIProviders: IAIProvider[] = [
@@ -41,7 +57,8 @@ const AIProviders: IAIProvider[] = [
41
57
  chatModel: ChromeAI,
42
58
  completer: ChromeCompleter,
43
59
  instructions: ChromeAIInstructions,
44
- settingsSchema: ChromeAISettings
60
+ settingsSchema: ChromeAISettings,
61
+ compatibilityCheck: chromeAICompatibilityCheck
45
62
  },
46
63
  {
47
64
  name: 'MistralAI',
@@ -50,6 +67,13 @@ const AIProviders: IAIProvider[] = [
50
67
  instructions: MistralAIInstructions,
51
68
  settingsSchema: MistralAISettings
52
69
  },
70
+ {
71
+ name: 'Ollama',
72
+ chatModel: ChatOllama,
73
+ completer: OllamaCompleter,
74
+ instructions: OllamaInstructions,
75
+ settingsSchema: OllamaAISettings
76
+ },
53
77
  {
54
78
  name: 'OpenAI',
55
79
  chatModel: ChatOpenAI,
@@ -58,14 +82,127 @@ const AIProviders: IAIProvider[] = [
58
82
  }
59
83
  ];
60
84
 
61
- export const defaultProviderPlugins: JupyterFrontEndPlugin<void>[] =
62
- AIProviders.map(provider => {
63
- return {
64
- id: `@jupyterlite/ai:${provider.name}`,
65
- autoStart: true,
66
- requires: [IAIProviderRegistry],
67
- activate: (app: JupyterFrontEnd, registry: IAIProviderRegistry) => {
68
- registry.add(provider);
85
+ /**
86
+ * Register the WebLLM provider in a separate plugin since it creates notifications
87
+ * when the model is changed in the settings.
88
+ */
89
+ const webLLMProviderPlugin: JupyterFrontEndPlugin<void> = {
90
+ id: '@jupyterlite/ai:webllm',
91
+ description: 'Register the WebLLM provider',
92
+ autoStart: true,
93
+ requires: [IAIProviderRegistry],
94
+ activate: (app: JupyterFrontEnd, registry: IAIProviderRegistry) => {
95
+ registry.add({
96
+ name: 'WebLLM',
97
+ chatModel: ChatWebLLM,
98
+ completer: WebLLMCompleter,
99
+ settingsSchema: WebLLMSettings,
100
+ instructions: WebLLMInstructions,
101
+ compatibilityCheck: webLLMCompatibilityCheck,
102
+ exposeChatModel: true
103
+ });
104
+
105
+ registry.providerChanged.connect(async (sender, args) => {
106
+ const { currentName, currentChatModel, chatError } = registry;
107
+ if (currentChatModel === null) {
108
+ Notification.emit(chatError, 'error', {
109
+ autoClose: 2000
110
+ });
111
+ return;
69
112
  }
70
- };
71
- });
113
+
114
+ // TODO: implement a proper way to handle models that may need to be initialized before being used.
115
+ // Mostly applies to WebLLM and ChromeAI as they may need to download the model in the browser first.
116
+ if (currentName === 'WebLLM') {
117
+ const compatibilityError = await webLLMCompatibilityCheck();
118
+
119
+ if (compatibilityError) {
120
+ Notification.dismiss();
121
+ Notification.emit(compatibilityError, 'error', {
122
+ autoClose: 2000
123
+ });
124
+ return;
125
+ }
126
+
127
+ const model = currentChatModel as ChatWebLLM;
128
+ if (model === null || !model.model) {
129
+ return;
130
+ }
131
+
132
+ // Find if the model is part of the prebuiltAppConfig
133
+ const modelRecord = prebuiltAppConfig.model_list.find(
134
+ modelRecord => modelRecord.model_id === model.model
135
+ );
136
+ if (!modelRecord) {
137
+ Notification.dismiss();
138
+ Notification.emit(
139
+ `Model ${model.model} not found in the prebuiltAppConfig`,
140
+ 'error',
141
+ {
142
+ autoClose: 2000
143
+ }
144
+ );
145
+ return;
146
+ }
147
+
148
+ // create a notification
149
+ const notification = Notification.emit(
150
+ 'Loading model...',
151
+ 'in-progress',
152
+ {
153
+ autoClose: false,
154
+ progress: 0
155
+ }
156
+ );
157
+ try {
158
+ void model.initialize(report => {
159
+ const { progress, text } = report;
160
+ if (progress === 1) {
161
+ Notification.update({
162
+ id: notification,
163
+ progress: 1,
164
+ message: `Model ${model.model} loaded successfully`,
165
+ type: 'success',
166
+ autoClose: 2000
167
+ });
168
+ return;
169
+ }
170
+ Notification.update({
171
+ id: notification,
172
+ progress: progress / 1,
173
+ message: text,
174
+ type: 'in-progress'
175
+ });
176
+ });
177
+ } catch (err) {
178
+ Notification.update({
179
+ id: notification,
180
+ progress: 1,
181
+ message: `Error loading model ${model.model}`,
182
+ type: 'error',
183
+ autoClose: 2000
184
+ });
185
+ }
186
+ }
187
+ });
188
+ }
189
+ };
190
+
191
+ /**
192
+ * Register all default AI providers.
193
+ */
194
+ const aiProviderPlugins = AIProviders.map(provider => {
195
+ return {
196
+ id: `@jupyterlite/ai:${provider.name}`,
197
+ autoStart: true,
198
+ requires: [IAIProviderRegistry],
199
+ activate: (app: JupyterFrontEnd, registry: IAIProviderRegistry) => {
200
+ registry.add(provider);
201
+ }
202
+ };
203
+ });
204
+
205
+ export const defaultProviderPlugins: JupyterFrontEndPlugin<void>[] = [
206
+ webLLMProviderPlugin,
207
+ ...aiProviderPlugins
208
+ ];
package/src/index.ts CHANGED
@@ -15,19 +15,16 @@ import { ReactWidget, IThemeManager } from '@jupyterlab/apputils';
15
15
  import { ICompletionProviderManager } from '@jupyterlab/completer';
16
16
  import { INotebookTracker } from '@jupyterlab/notebook';
17
17
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
18
- import {
19
- ISettingConnector,
20
- ISettingRegistry
21
- } from '@jupyterlab/settingregistry';
18
+ import { ISettingRegistry } from '@jupyterlab/settingregistry';
22
19
  import { IFormRendererRegistry } from '@jupyterlab/ui-components';
23
20
  import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
24
21
  import { ISecretsManager, SecretsManager } from 'jupyter-secrets-manager';
25
22
 
26
- import { ChatHandler } from './chat-handler';
23
+ import { ChatHandler, welcomeMessage } from './chat-handler';
27
24
  import { CompletionProvider } from './completion-provider';
28
25
  import { defaultProviderPlugins } from './default-providers';
29
26
  import { AIProviderRegistry } from './provider';
30
- import { aiSettingsRenderer, SettingConnector } from './settings';
27
+ import { aiSettingsRenderer } from './settings';
31
28
  import { IAIProviderRegistry, PLUGIN_IDS } from './tokens';
32
29
  import { stopItem } from './components/stop-button';
33
30
 
@@ -109,9 +106,11 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
109
106
  const stopButton = stopItem(() => chatHandler.stopStreaming());
110
107
  inputToolbarRegistry.addItem('stop', stopButton);
111
108
 
112
- chatHandler.writersChanged.connect((_, users) => {
109
+ chatHandler.writersChanged.connect((_, writers) => {
113
110
  if (
114
- users.filter(user => user.username === chatHandler.personaName).length
111
+ writers.filter(
112
+ writer => writer.user.username === chatHandler.personaName
113
+ ).length
115
114
  ) {
116
115
  inputToolbarRegistry.hide('send');
117
116
  inputToolbarRegistry.show('stop');
@@ -127,7 +126,8 @@ const chatPlugin: JupyterFrontEndPlugin<void> = {
127
126
  themeManager,
128
127
  rmRegistry,
129
128
  chatCommandRegistry,
130
- inputToolbarRegistry
129
+ inputToolbarRegistry,
130
+ welcomeMessage: welcomeMessage(providerRegistry.providers)
131
131
  });
132
132
  chatWidget.title.caption = 'Jupyterlite AI Chat';
133
133
  } catch (e) {
@@ -162,15 +162,14 @@ const providerRegistryPlugin: JupyterFrontEndPlugin<IAIProviderRegistry> =
162
162
  id: PLUGIN_IDS.providerRegistry,
163
163
  autoStart: true,
164
164
  requires: [IFormRendererRegistry, ISettingRegistry],
165
- optional: [IRenderMimeRegistry, ISecretsManager, ISettingConnector],
165
+ optional: [IRenderMimeRegistry, ISecretsManager],
166
166
  provides: IAIProviderRegistry,
167
167
  activate: (
168
168
  app: JupyterFrontEnd,
169
169
  editorRegistry: IFormRendererRegistry,
170
170
  settingRegistry: ISettingRegistry,
171
171
  rmRegistry?: IRenderMimeRegistry,
172
- secretsManager?: ISecretsManager,
173
- settingConnector?: ISettingConnector
172
+ secretsManager?: ISecretsManager
174
173
  ): IAIProviderRegistry => {
175
174
  const providerRegistry = new AIProviderRegistry({
176
175
  token,
@@ -183,14 +182,16 @@ const providerRegistryPlugin: JupyterFrontEndPlugin<IAIProviderRegistry> =
183
182
  providerRegistry,
184
183
  secretsToken: token,
185
184
  rmRegistry,
186
- secretsManager,
187
- settingConnector
185
+ secretsManager
188
186
  })
189
187
  );
190
188
 
191
189
  settingRegistry
192
190
  .load(providerRegistryPlugin.id)
193
191
  .then(settings => {
192
+ if (!secretsManager) {
193
+ delete settings.schema.properties?.['UseSecretsManager'];
194
+ }
194
195
  const updateProvider = () => {
195
196
  // Update the settings to the AI providers.
196
197
  const providerSettings = (settings.get('AIprovider').composite ?? {
@@ -216,25 +217,12 @@ const providerRegistryPlugin: JupyterFrontEndPlugin<IAIProviderRegistry> =
216
217
  }
217
218
  }));
218
219
 
219
- /**
220
- * Provides the settings connector as a separate plugin to allow for alternative
221
- * implementations that may want to fetch settings from a different source or
222
- * endpoint.
223
- */
224
- const settingsConnector: JupyterFrontEndPlugin<ISettingConnector> = {
225
- id: PLUGIN_IDS.settingsConnector,
226
- description: 'Provides a settings connector which does not save passwords.',
227
- autoStart: true,
228
- provides: ISettingConnector,
229
- activate: (app: JupyterFrontEnd) =>
230
- new SettingConnector(app.serviceManager.settings)
231
- };
232
-
233
220
  export default [
234
221
  providerRegistryPlugin,
235
222
  chatCommandRegistryPlugin,
236
223
  chatPlugin,
237
224
  completerPlugin,
238
- settingsConnector,
239
225
  ...defaultProviderPlugins
240
226
  ];
227
+
228
+ export { IAIProviderRegistry } from './tokens';
package/src/provider.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ CompletionHandler,
3
+ IInlineCompletionContext
4
+ } from '@jupyterlab/completer';
1
5
  import { BaseLanguageModel } from '@langchain/core/language_models/base';
2
6
  import { BaseChatModel } from '@langchain/core/language_models/chat_models';
3
7
  import { ISignal, Signal } from '@lumino/signaling';
@@ -14,6 +18,7 @@ import {
14
18
  ISetProviderOptions,
15
19
  PLUGIN_IDS
16
20
  } from './tokens';
21
+ import { AIChatModel, AICompleter } from './types/ai-model';
17
22
 
18
23
  const SECRETS_NAMESPACE = PLUGIN_IDS.providerRegistry;
19
24
 
@@ -42,9 +47,10 @@ The code is written in JupyterLab, a data analysis and code development
42
47
  environment which can execute code extended with additional syntax for
43
48
  interactive features, such as magics.
44
49
  Only give raw strings back, do not format the response using backticks.
45
- The output should be a single string, and should correspond to what a human users
46
- would write.
50
+ The output should be a single string, and should only contain the code that will complete the
51
+ give code passed as input, no explanation whatsoever.
47
52
  Do not include the prompt in the output, only the string that should be appended to the current input.
53
+ Here is the code to complete:
48
54
  `;
49
55
 
50
56
  export class AIProviderRegistry implements IAIProviderRegistry {
@@ -60,19 +66,19 @@ export class AIProviderRegistry implements IAIProviderRegistry {
60
66
  * Get the list of provider names.
61
67
  */
62
68
  get providers(): string[] {
63
- return Array.from(this._providers.keys());
69
+ return Array.from(Private.providers.keys());
64
70
  }
65
71
 
66
72
  /**
67
73
  * Add a new provider.
68
74
  */
69
75
  add(provider: IAIProvider): void {
70
- if (this._providers.has(provider.name)) {
76
+ if (Private.providers.has(provider.name)) {
71
77
  throw new Error(
72
78
  `A AI provider named '${provider.name}' is already registered`
73
79
  );
74
80
  }
75
- this._providers.set(provider.name, provider);
81
+ Private.providers.set(provider.name, provider);
76
82
 
77
83
  // Set the provider if the loading has been deferred.
78
84
  if (provider.name === this._deferredProvider?.name) {
@@ -84,34 +90,57 @@ export class AIProviderRegistry implements IAIProviderRegistry {
84
90
  * Get the current provider name.
85
91
  */
86
92
  get currentName(): string {
87
- return this._name;
93
+ return Private.getName();
88
94
  }
89
95
 
90
96
  /**
91
- * Get the current completer of the completion provider.
97
+ * Get the current AICompleter.
92
98
  */
93
- get currentCompleter(): IBaseCompleter | null {
94
- if (this._name === 'None') {
99
+ get currentCompleter(): AICompleter | null {
100
+ if (Private.getName() === 'None') {
101
+ return null;
102
+ }
103
+ const completer = Private.getCompleter();
104
+ if (completer === null) {
95
105
  return null;
96
106
  }
97
- return this._completer;
107
+ return {
108
+ fetch: (
109
+ request: CompletionHandler.IRequest,
110
+ context: IInlineCompletionContext
111
+ ) => completer.fetch(request, context)
112
+ };
98
113
  }
99
114
 
100
115
  /**
101
- * Get the current llm chat model.
116
+ * Get the current AIChatModel.
102
117
  */
103
- get currentChatModel(): BaseChatModel | null {
104
- if (this._name === 'None') {
118
+ get currentChatModel(): AIChatModel | null {
119
+ if (Private.getName() === 'None') {
120
+ return null;
121
+ }
122
+ const currentProvider = Private.providers.get(Private.getName()) ?? null;
123
+
124
+ const chatModel = Private.getChatModel();
125
+ if (chatModel === null) {
105
126
  return null;
106
127
  }
107
- return this._chatModel;
128
+ if (currentProvider?.exposeChatModel ?? false) {
129
+ // Expose the full chat model if expected.
130
+ return chatModel as AIChatModel;
131
+ }
132
+
133
+ // Otherwise, we create a reduced AIChatModel interface.
134
+ return {
135
+ stream: (input: any, options?: any) => chatModel.stream(input, options)
136
+ };
108
137
  }
109
138
 
110
139
  /**
111
140
  * Get the settings schema of a given provider.
112
141
  */
113
142
  getSettingsSchema(provider: string): JSONSchema7 {
114
- return (this._providers.get(provider)?.settingsSchema?.properties ||
143
+ return (Private.providers.get(provider)?.settingsSchema?.properties ||
115
144
  {}) as JSONSchema7;
116
145
  }
117
146
 
@@ -119,15 +148,25 @@ export class AIProviderRegistry implements IAIProviderRegistry {
119
148
  * Get the instructions of a given provider.
120
149
  */
121
150
  getInstructions(provider: string): string | undefined {
122
- return this._providers.get(provider)?.instructions;
151
+ return Private.providers.get(provider)?.instructions;
152
+ }
153
+
154
+ /**
155
+ * Get the compatibility check function of a given provider.
156
+ */
157
+ getCompatibilityCheck(
158
+ provider: string
159
+ ): (() => Promise<string | null>) | undefined {
160
+ return Private.providers.get(provider)?.compatibilityCheck;
123
161
  }
124
162
 
125
163
  /**
126
164
  * Format an error message from the current provider.
127
165
  */
128
166
  formatErrorMessage(error: any): string {
129
- if (this._currentProvider?.errorMessage) {
130
- return this._currentProvider?.errorMessage(error);
167
+ const currentProvider = Private.providers.get(Private.getName()) ?? null;
168
+ if (currentProvider?.errorMessage) {
169
+ return currentProvider?.errorMessage(error);
131
170
  }
132
171
  if (error.message) {
133
172
  return error.message;
@@ -143,7 +182,7 @@ export class AIProviderRegistry implements IAIProviderRegistry {
143
182
  }
144
183
 
145
184
  /**
146
- * get the current completer error.
185
+ * Get the current completer error.
147
186
  */
148
187
  get completerError(): string {
149
188
  return this._completerError;
@@ -157,8 +196,8 @@ export class AIProviderRegistry implements IAIProviderRegistry {
157
196
  */
158
197
  async setProvider(options: ISetProviderOptions): Promise<void> {
159
198
  const { name, settings } = options;
160
- this._currentProvider = this._providers.get(name) ?? null;
161
- if (this._currentProvider === null) {
199
+ const currentProvider = Private.providers.get(name) ?? null;
200
+ if (currentProvider === null) {
162
201
  // The current provider may not be loaded when the settings are first loaded.
163
202
  // Let's defer the provider loading.
164
203
  this._deferredProvider = options;
@@ -166,6 +205,23 @@ export class AIProviderRegistry implements IAIProviderRegistry {
166
205
  this._deferredProvider = null;
167
206
  }
168
207
 
208
+ const compatibilityCheck = this.getCompatibilityCheck(name);
209
+ if (compatibilityCheck !== undefined) {
210
+ const error = await compatibilityCheck();
211
+ if (error !== null) {
212
+ this._chatError = error.trim();
213
+ this._completerError = error.trim();
214
+ Private.setName('None');
215
+ this._providerChanged.emit();
216
+ return;
217
+ }
218
+ }
219
+
220
+ if (name === 'None') {
221
+ this._chatError = '';
222
+ this._completerError = '';
223
+ }
224
+
169
225
  // Build a new settings object containing the secrets.
170
226
  const fullSettings: IDict = {};
171
227
  for (const key of Object.keys(settings)) {
@@ -184,33 +240,37 @@ export class AIProviderRegistry implements IAIProviderRegistry {
184
240
  fullSettings[key] = settings[key];
185
241
  }
186
242
 
187
- if (this._currentProvider?.completer !== undefined) {
243
+ if (currentProvider?.completer !== undefined) {
188
244
  try {
189
- this._completer = new this._currentProvider.completer({
190
- settings: fullSettings
191
- });
245
+ Private.setCompleter(
246
+ new currentProvider.completer({
247
+ settings: fullSettings
248
+ })
249
+ );
192
250
  this._completerError = '';
193
251
  } catch (e: any) {
194
252
  this._completerError = e.message;
195
253
  }
196
254
  } else {
197
- this._completer = null;
255
+ Private.setCompleter(null);
198
256
  }
199
257
 
200
- if (this._currentProvider?.chatModel !== undefined) {
258
+ if (currentProvider?.chatModel !== undefined) {
201
259
  try {
202
- this._chatModel = new this._currentProvider.chatModel({
203
- ...fullSettings
204
- });
260
+ Private.setChatModel(
261
+ new currentProvider.chatModel({
262
+ ...fullSettings
263
+ })
264
+ );
205
265
  this._chatError = '';
206
266
  } catch (e: any) {
207
267
  this._chatError = e.message;
208
- this._chatModel = null;
268
+ Private.setChatModel(null);
209
269
  }
210
270
  } else {
211
- this._chatModel = null;
271
+ Private.setChatModel(null);
212
272
  }
213
- this._name = name;
273
+ Private.setName(name);
214
274
  this._providerChanged.emit();
215
275
  }
216
276
 
@@ -222,14 +282,9 @@ export class AIProviderRegistry implements IAIProviderRegistry {
222
282
  }
223
283
 
224
284
  private _secretsManager: ISecretsManager | null;
225
- private _currentProvider: IAIProvider | null = null;
226
- private _completer: IBaseCompleter | null = null;
227
- private _chatModel: BaseChatModel | null = null;
228
- private _name: string = 'None';
229
285
  private _providerChanged = new Signal<IAIProviderRegistry, void>(this);
230
286
  private _chatError: string = '';
231
287
  private _completerError: string = '';
232
- private _providers = new Map<string, IAIProvider>();
233
288
  private _deferredProvider: ISetProviderOptions | null = null;
234
289
  }
235
290
 
@@ -303,21 +358,53 @@ export namespace AIProviderRegistry {
303
358
 
304
359
  namespace Private {
305
360
  /**
306
- * The token to use with the secrets manager.
361
+ * The token to use with the secrets manager, setter and getter.
307
362
  */
308
363
  let secretsToken: symbol;
364
+ export function setToken(value: symbol): void {
365
+ secretsToken = value;
366
+ }
367
+ export function getToken(): symbol {
368
+ return secretsToken;
369
+ }
309
370
 
310
371
  /**
311
- * Set of the token.
372
+ * The providers map, in private namespace to prevent updating the 'exposeChatModel'
373
+ * flag.
312
374
  */
313
- export function setToken(value: symbol): void {
314
- secretsToken = value;
375
+ export const providers = new Map<string, IAIProvider>();
376
+
377
+ /**
378
+ * The name of the current provider, setter and getter.
379
+ * It is in a private namespace to prevent updating it without updating the models.
380
+ */
381
+ let name: string = 'None';
382
+ export function setName(value: string): void {
383
+ name = value;
384
+ }
385
+ export function getName(): string {
386
+ return name;
315
387
  }
316
388
 
317
389
  /**
318
- * get the token.
390
+ * The chat model setter and getter.
319
391
  */
320
- export function getToken(): symbol {
321
- return secretsToken;
392
+ let chatModel: BaseChatModel | null = null;
393
+ export function setChatModel(model: BaseChatModel | null): void {
394
+ chatModel = model;
395
+ }
396
+ export function getChatModel(): BaseChatModel | null {
397
+ return chatModel;
398
+ }
399
+
400
+ /**
401
+ * The completer setter and getter.
402
+ */
403
+ let completer: IBaseCompleter | null = null;
404
+ export function setCompleter(model: IBaseCompleter | null): void {
405
+ completer = model;
406
+ }
407
+ export function getCompleter(): IBaseCompleter | null {
408
+ return completer;
322
409
  }
323
410
  }
@@ -1,3 +1,2 @@
1
1
  export * from './panel';
2
- export * from './settings-connector';
3
2
  export * from './utils';