@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.
- package/README.md +1 -1
- package/lib/base-completer.d.ts +0 -5
- package/lib/chat-handler.d.ts +19 -5
- package/lib/chat-handler.js +47 -26
- package/lib/completion-provider.d.ts +2 -2
- package/lib/completion-provider.js +4 -3
- package/lib/components/stop-button.d.ts +0 -1
- package/lib/default-providers/Anthropic/completer.d.ts +0 -2
- package/lib/default-providers/Anthropic/completer.js +2 -4
- package/lib/default-providers/ChromeAI/completer.d.ts +0 -2
- package/lib/default-providers/ChromeAI/completer.js +2 -4
- package/lib/default-providers/ChromeAI/instructions.d.ts +4 -0
- package/lib/default-providers/ChromeAI/instructions.js +18 -0
- package/lib/default-providers/MistralAI/completer.d.ts +0 -2
- package/lib/default-providers/MistralAI/completer.js +3 -4
- package/lib/default-providers/Ollama/completer.d.ts +17 -0
- package/lib/default-providers/Ollama/completer.js +49 -0
- package/lib/default-providers/Ollama/instructions.d.ts +2 -0
- package/lib/default-providers/Ollama/instructions.js +70 -0
- package/lib/default-providers/Ollama/settings-schema.json +146 -0
- package/lib/default-providers/OpenAI/completer.d.ts +0 -2
- package/lib/default-providers/OpenAI/completer.js +2 -4
- package/lib/default-providers/WebLLM/completer.d.ts +27 -0
- package/lib/default-providers/WebLLM/completer.js +136 -0
- package/lib/default-providers/WebLLM/instructions.d.ts +6 -0
- package/lib/default-providers/WebLLM/instructions.js +32 -0
- package/lib/default-providers/WebLLM/settings-schema.json +21 -0
- package/lib/default-providers/index.js +119 -4
- package/lib/index.d.ts +2 -2
- package/lib/index.js +16 -26
- package/lib/provider.d.ts +11 -13
- package/lib/provider.js +120 -52
- package/lib/settings/index.d.ts +0 -1
- package/lib/settings/index.js +0 -1
- package/lib/settings/panel.d.ts +37 -8
- package/lib/settings/panel.js +225 -131
- package/lib/tokens.d.ts +21 -2
- package/lib/types/ai-model.d.ts +24 -0
- package/lib/types/ai-model.js +5 -0
- package/package.json +14 -11
- package/schema/provider-registry.json +0 -6
- package/src/base-completer.ts +0 -6
- package/src/chat-handler.ts +40 -7
- package/src/completion-provider.ts +2 -2
- package/src/default-providers/Anthropic/completer.ts +0 -5
- package/src/default-providers/ChromeAI/completer.ts +0 -5
- package/src/default-providers/ChromeAI/instructions.ts +21 -0
- package/src/default-providers/MistralAI/completer.ts +0 -5
- package/src/default-providers/Ollama/completer.ts +62 -0
- package/src/default-providers/Ollama/instructions.ts +70 -0
- package/src/default-providers/OpenAI/completer.ts +0 -5
- package/src/default-providers/WebLLM/completer.ts +162 -0
- package/src/default-providers/WebLLM/instructions.ts +33 -0
- package/src/default-providers/index.ts +151 -14
- package/src/index.ts +17 -29
- package/src/provider.ts +132 -45
- package/src/settings/index.ts +0 -1
- package/src/settings/panel.tsx +207 -73
- package/src/tokens.ts +23 -2
- package/src/types/ai-model.ts +37 -0
- package/src/types/service-worker.d.ts +6 -0
- package/style/base.css +5 -0
- package/lib/settings/settings-connector.d.ts +0 -31
- package/lib/settings/settings-connector.js +0 -61
- 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
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
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((_,
|
|
109
|
+
chatHandler.writersChanged.connect((_, writers) => {
|
|
113
110
|
if (
|
|
114
|
-
|
|
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
|
|
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
|
|
46
|
-
|
|
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(
|
|
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 (
|
|
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
|
-
|
|
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
|
|
93
|
+
return Private.getName();
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
/**
|
|
91
|
-
* Get the current
|
|
97
|
+
* Get the current AICompleter.
|
|
92
98
|
*/
|
|
93
|
-
get currentCompleter():
|
|
94
|
-
if (
|
|
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
|
|
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
|
|
116
|
+
* Get the current AIChatModel.
|
|
102
117
|
*/
|
|
103
|
-
get currentChatModel():
|
|
104
|
-
if (
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
130
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
161
|
-
if (
|
|
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 (
|
|
243
|
+
if (currentProvider?.completer !== undefined) {
|
|
188
244
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
255
|
+
Private.setCompleter(null);
|
|
198
256
|
}
|
|
199
257
|
|
|
200
|
-
if (
|
|
258
|
+
if (currentProvider?.chatModel !== undefined) {
|
|
201
259
|
try {
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
268
|
+
Private.setChatModel(null);
|
|
209
269
|
}
|
|
210
270
|
} else {
|
|
211
|
-
|
|
271
|
+
Private.setChatModel(null);
|
|
212
272
|
}
|
|
213
|
-
|
|
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
|
-
*
|
|
372
|
+
* The providers map, in private namespace to prevent updating the 'exposeChatModel'
|
|
373
|
+
* flag.
|
|
312
374
|
*/
|
|
313
|
-
export
|
|
314
|
-
|
|
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
|
-
*
|
|
390
|
+
* The chat model setter and getter.
|
|
319
391
|
*/
|
|
320
|
-
|
|
321
|
-
|
|
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
|
}
|
package/src/settings/index.ts
CHANGED