@jupyterlite/ai 0.6.2 → 0.8.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 (87) hide show
  1. package/README.md +1 -1
  2. package/lib/base-completer.d.ts +22 -5
  3. package/lib/base-completer.js +14 -1
  4. package/lib/chat-handler.d.ts +23 -11
  5. package/lib/chat-handler.js +66 -45
  6. package/lib/completion-provider.d.ts +2 -2
  7. package/lib/completion-provider.js +5 -4
  8. package/lib/components/stop-button.d.ts +0 -1
  9. package/lib/default-prompts.d.ts +2 -0
  10. package/lib/default-prompts.js +31 -0
  11. package/lib/default-providers/Anthropic/completer.d.ts +4 -11
  12. package/lib/default-providers/Anthropic/completer.js +5 -16
  13. package/lib/default-providers/ChromeAI/completer.d.ts +4 -11
  14. package/lib/default-providers/ChromeAI/completer.js +5 -16
  15. package/lib/default-providers/ChromeAI/instructions.d.ts +4 -0
  16. package/lib/default-providers/ChromeAI/instructions.js +18 -0
  17. package/lib/default-providers/ChromeAI/settings-schema.json +0 -3
  18. package/lib/default-providers/Gemini/completer.d.ts +12 -0
  19. package/lib/default-providers/Gemini/completer.js +48 -0
  20. package/lib/default-providers/Gemini/instructions.d.ts +2 -0
  21. package/lib/default-providers/Gemini/instructions.js +9 -0
  22. package/lib/default-providers/Gemini/settings-schema.json +64 -0
  23. package/lib/default-providers/MistralAI/completer.d.ts +10 -13
  24. package/lib/default-providers/MistralAI/completer.js +42 -52
  25. package/lib/default-providers/MistralAI/instructions.d.ts +1 -1
  26. package/lib/default-providers/MistralAI/instructions.js +2 -0
  27. package/lib/default-providers/Ollama/completer.d.ts +12 -0
  28. package/lib/default-providers/Ollama/completer.js +43 -0
  29. package/lib/default-providers/Ollama/instructions.d.ts +2 -0
  30. package/lib/default-providers/Ollama/instructions.js +70 -0
  31. package/lib/default-providers/Ollama/settings-schema.json +143 -0
  32. package/lib/default-providers/OpenAI/completer.d.ts +4 -11
  33. package/lib/default-providers/OpenAI/completer.js +8 -16
  34. package/lib/default-providers/OpenAI/settings-schema.json +88 -128
  35. package/lib/default-providers/WebLLM/completer.d.ts +21 -0
  36. package/lib/default-providers/WebLLM/completer.js +127 -0
  37. package/lib/default-providers/WebLLM/instructions.d.ts +6 -0
  38. package/lib/default-providers/WebLLM/instructions.js +32 -0
  39. package/lib/default-providers/WebLLM/settings-schema.json +19 -0
  40. package/lib/default-providers/index.js +127 -8
  41. package/lib/index.d.ts +3 -2
  42. package/lib/index.js +80 -36
  43. package/lib/provider.d.ts +48 -22
  44. package/lib/provider.js +254 -101
  45. package/lib/settings/index.d.ts +1 -1
  46. package/lib/settings/index.js +1 -1
  47. package/lib/settings/panel.d.ts +151 -14
  48. package/lib/settings/panel.js +334 -145
  49. package/lib/settings/textarea.d.ts +2 -0
  50. package/lib/settings/textarea.js +18 -0
  51. package/lib/tokens.d.ts +45 -22
  52. package/lib/tokens.js +2 -1
  53. package/lib/types/ai-model.d.ts +24 -0
  54. package/lib/types/ai-model.js +5 -0
  55. package/package.json +19 -15
  56. package/schema/chat.json +1 -1
  57. package/schema/provider-registry.json +8 -8
  58. package/schema/system-prompts.json +22 -0
  59. package/src/base-completer.ts +38 -6
  60. package/src/chat-handler.ts +62 -31
  61. package/src/completion-provider.ts +3 -3
  62. package/src/default-prompts.ts +33 -0
  63. package/src/default-providers/Anthropic/completer.ts +5 -21
  64. package/src/default-providers/ChromeAI/completer.ts +5 -21
  65. package/src/default-providers/ChromeAI/instructions.ts +21 -0
  66. package/src/default-providers/Gemini/completer.ts +61 -0
  67. package/src/default-providers/Gemini/instructions.ts +9 -0
  68. package/src/default-providers/MistralAI/completer.ts +47 -65
  69. package/src/default-providers/MistralAI/instructions.ts +2 -0
  70. package/src/default-providers/Ollama/completer.ts +54 -0
  71. package/src/default-providers/Ollama/instructions.ts +70 -0
  72. package/src/default-providers/OpenAI/completer.ts +8 -21
  73. package/src/default-providers/WebLLM/completer.ts +151 -0
  74. package/src/default-providers/WebLLM/instructions.ts +33 -0
  75. package/src/default-providers/index.ts +158 -18
  76. package/src/index.ts +108 -40
  77. package/src/provider.ts +300 -109
  78. package/src/settings/index.ts +1 -1
  79. package/src/settings/panel.tsx +463 -101
  80. package/src/settings/textarea.tsx +33 -0
  81. package/src/tokens.ts +49 -24
  82. package/src/types/ai-model.ts +37 -0
  83. package/src/types/service-worker.d.ts +6 -0
  84. package/style/base.css +34 -0
  85. package/lib/settings/settings-connector.d.ts +0 -31
  86. package/lib/settings/settings-connector.js +0 -61
  87. package/src/settings/settings-connector.ts +0 -88
package/lib/provider.js CHANGED
@@ -1,116 +1,130 @@
1
+ import { Notification } from '@jupyterlab/apputils';
2
+ import { Debouncer } from '@lumino/polling';
1
3
  import { Signal } from '@lumino/signaling';
2
4
  import { getSecretId, SECRETS_REPLACEMENT } from './settings';
3
5
  import { PLUGIN_IDS } from './tokens';
4
6
  const SECRETS_NAMESPACE = PLUGIN_IDS.providerRegistry;
5
- export const chatSystemPrompt = (options) => `
6
- You are Jupyternaut, a conversational assistant living in JupyterLab to help users.
7
- You are not a language model, but rather an application built on a foundation model from ${options.provider_name}.
8
- You are talkative and you provide lots of specific details from the foundation model's context.
9
- You may use Markdown to format your response.
10
- If your response includes code, they must be enclosed in Markdown fenced code blocks (with triple backticks before and after).
11
- If your response includes mathematical notation, they must be expressed in LaTeX markup and enclosed in LaTeX delimiters.
12
- All dollar quantities (of USD) must be formatted in LaTeX, with the \`$\` symbol escaped by a single backslash \`\\\`.
13
- - Example prompt: \`If I have \\\\$100 and spend \\\\$20, how much money do I have left?\`
14
- - **Correct** response: \`You have \\(\\$80\\) remaining.\`
15
- - **Incorrect** response: \`You have $80 remaining.\`
16
- If you do not know the answer to a question, answer truthfully by responding that you do not know.
17
- The following is a friendly conversation between you and a human.
18
- `;
19
- export const COMPLETION_SYSTEM_PROMPT = `
20
- You are an application built to provide helpful code completion suggestions.
21
- You should only produce code. Keep comments to minimum, use the
22
- programming language comment syntax. Produce clean code.
23
- The code is written in JupyterLab, a data analysis and code development
24
- environment which can execute code extended with additional syntax for
25
- interactive features, such as magics.
26
- Only give raw strings back, do not format the response using backticks.
27
- The output should be a single string, and should correspond to what a human users
28
- would write.
29
- Do not include the prompt in the output, only the string that should be appended to the current input.
30
- `;
7
+ const NOTIFICATION_DELAY = 2000;
31
8
  export class AIProviderRegistry {
32
9
  /**
33
10
  * The constructor of the provider registry.
34
11
  */
35
12
  constructor(options) {
36
- this._currentProvider = null;
37
- this._completer = null;
38
- this._chatModel = null;
39
- this._name = 'None';
40
- this._providerChanged = new Signal(this);
41
- this._chatError = '';
42
- this._completerError = '';
43
- this._providers = new Map();
44
- this._deferredProvider = null;
45
13
  this._secretsManager = options.secretsManager || null;
46
14
  Private.setToken(options.token);
15
+ this._notifications = {
16
+ chat: new Debouncer(this._emitErrorNotification, NOTIFICATION_DELAY),
17
+ completer: new Debouncer(this._emitErrorNotification, NOTIFICATION_DELAY)
18
+ };
47
19
  }
48
20
  /**
49
21
  * Get the list of provider names.
50
22
  */
51
23
  get providers() {
52
- return Array.from(this._providers.keys());
24
+ return Array.from(Private.providers.keys());
53
25
  }
54
26
  /**
55
27
  * Add a new provider.
56
28
  */
57
29
  add(provider) {
58
- var _a;
59
- if (this._providers.has(provider.name)) {
30
+ if (Private.providers.has(provider.name)) {
60
31
  throw new Error(`A AI provider named '${provider.name}' is already registered`);
61
32
  }
62
- this._providers.set(provider.name, provider);
63
- // Set the provider if the loading has been deferred.
64
- if (provider.name === ((_a = this._deferredProvider) === null || _a === void 0 ? void 0 : _a.name)) {
65
- this.setProvider(this._deferredProvider);
33
+ Private.providers.set(provider.name, provider);
34
+ // Set the providers if the loading has been deferred.
35
+ if (provider.name === this._deferredProvider.completer?.name) {
36
+ this.setCompleterProvider(this._deferredProvider.completer);
37
+ }
38
+ if (provider.name === this._deferredProvider.chat?.name) {
39
+ this.setChatProvider(this._deferredProvider.chat);
66
40
  }
67
41
  }
68
42
  /**
69
43
  * Get the current provider name.
70
44
  */
71
- get currentName() {
72
- return this._name;
45
+ currentName(role) {
46
+ return Private.getName(role);
73
47
  }
74
48
  /**
75
- * Get the current completer of the completion provider.
49
+ * Get the current AICompleter.
76
50
  */
77
51
  get currentCompleter() {
78
- if (this._name === 'None') {
52
+ if (Private.getName('completer') === 'None') {
53
+ return null;
54
+ }
55
+ const completer = Private.getCompleter();
56
+ if (completer === null) {
79
57
  return null;
80
58
  }
81
- return this._completer;
59
+ return {
60
+ fetch: (request, context) => completer.fetch(request, context)
61
+ };
82
62
  }
83
63
  /**
84
- * Get the current llm chat model.
64
+ * Getter/setter for the completer system prompt.
65
+ */
66
+ get completerSystemPrompt() {
67
+ return this._completerPrompt.replaceAll('$provider_name$', this.currentName('completer'));
68
+ }
69
+ set completerSystemPrompt(value) {
70
+ this._completerPrompt = value;
71
+ }
72
+ /**
73
+ * Get the current AIChatModel.
85
74
  */
86
75
  get currentChatModel() {
87
- if (this._name === 'None') {
76
+ if (Private.getName('chat') === 'None') {
77
+ return null;
78
+ }
79
+ const currentProvider = Private.providers.get(Private.getName('chat')) ?? null;
80
+ const chatModel = Private.getChatModel();
81
+ if (chatModel === null) {
88
82
  return null;
89
83
  }
90
- return this._chatModel;
84
+ if (currentProvider?.exposeChatModel ?? false) {
85
+ // Expose the full chat model if expected.
86
+ return chatModel;
87
+ }
88
+ // Otherwise, we create a reduced AIChatModel interface.
89
+ return {
90
+ stream: (input, options) => chatModel.stream(input, options)
91
+ };
92
+ }
93
+ /**
94
+ * Getter/setter for the chat system prompt.
95
+ */
96
+ get chatSystemPrompt() {
97
+ return this._chatPrompt.replaceAll('$provider_name$', this.currentName('chat'));
98
+ }
99
+ set chatSystemPrompt(value) {
100
+ this._chatPrompt = value;
91
101
  }
92
102
  /**
93
103
  * Get the settings schema of a given provider.
94
104
  */
95
105
  getSettingsSchema(provider) {
96
- var _a, _b;
97
- return (((_b = (_a = this._providers.get(provider)) === null || _a === void 0 ? void 0 : _a.settingsSchema) === null || _b === void 0 ? void 0 : _b.properties) ||
106
+ return (Private.providers.get(provider)?.settingsSchema?.properties ||
98
107
  {});
99
108
  }
100
109
  /**
101
110
  * Get the instructions of a given provider.
102
111
  */
103
112
  getInstructions(provider) {
104
- var _a;
105
- return (_a = this._providers.get(provider)) === null || _a === void 0 ? void 0 : _a.instructions;
113
+ return Private.providers.get(provider)?.instructions;
114
+ }
115
+ /**
116
+ * Get the compatibility check function of a given provider.
117
+ */
118
+ getCompatibilityCheck(provider) {
119
+ return Private.providers.get(provider)?.compatibilityCheck;
106
120
  }
107
121
  /**
108
122
  * Format an error message from the current provider.
109
123
  */
110
124
  formatErrorMessage(error) {
111
- var _a, _b;
112
- if ((_a = this._currentProvider) === null || _a === void 0 ? void 0 : _a.errorMessage) {
113
- return (_b = this._currentProvider) === null || _b === void 0 ? void 0 : _b.errorMessage(error);
125
+ const currentProvider = Private.providers.get(Private.getName('chat')) ?? null;
126
+ if (currentProvider?.errorMessage) {
127
+ return currentProvider?.errorMessage(error);
114
128
  }
115
129
  if (error.message) {
116
130
  return error.message;
@@ -118,79 +132,149 @@ export class AIProviderRegistry {
118
132
  return error;
119
133
  }
120
134
  /**
121
- * Get the current chat error;
135
+ * Get/set the current chat error;
122
136
  */
123
137
  get chatError() {
124
138
  return this._chatError;
125
139
  }
140
+ set chatError(error) {
141
+ this._chatError = error;
142
+ if (error !== '') {
143
+ this._notifications.chat.invoke(`Chat: ${error}`);
144
+ }
145
+ }
126
146
  /**
127
- * get the current completer error.
147
+ * Get/set the current completer error.
128
148
  */
129
149
  get completerError() {
130
150
  return this._completerError;
131
151
  }
152
+ set completerError(error) {
153
+ this._completerError = error;
154
+ if (error !== '') {
155
+ this._notifications.completer.invoke(`Completer: ${error}`);
156
+ }
157
+ }
132
158
  /**
133
- * Set the providers (chat model and completer).
134
- * Creates the providers if the name has changed, otherwise only updates their config.
159
+ * A function to emit a notification error.
160
+ */
161
+ _emitErrorNotification(error) {
162
+ Notification.emit(error, 'error', {
163
+ autoClose: NOTIFICATION_DELAY
164
+ });
165
+ }
166
+ /**
167
+ * Set the completer provider.
168
+ * Creates the provider if the name has changed, otherwise only updates its config.
135
169
  *
136
170
  * @param options - An object with the name and the settings of the provider to use.
137
171
  */
138
- async setProvider(options) {
139
- var _a, _b, _c, _d;
140
- const { name, settings } = options;
141
- this._currentProvider = (_a = this._providers.get(name)) !== null && _a !== void 0 ? _a : null;
142
- if (this._currentProvider === null) {
172
+ async setCompleterProvider(settings) {
173
+ this.completerError = '';
174
+ if (!Object.keys(settings).includes('provider')) {
175
+ Private.setName('completer', 'None');
176
+ Private.setCompleter(null);
177
+ this.completerError =
178
+ 'The provider is missing from the completer settings';
179
+ return;
180
+ }
181
+ const provider = settings['provider'];
182
+ const currentProvider = Private.providers.get(provider) ?? null;
183
+ if (currentProvider === null) {
143
184
  // The current provider may not be loaded when the settings are first loaded.
144
185
  // Let's defer the provider loading.
145
- this._deferredProvider = options;
186
+ this._deferredProvider.completer = settings;
187
+ Private.setName('completer', provider);
188
+ Private.setCompleter(null);
189
+ return;
146
190
  }
147
191
  else {
148
- this._deferredProvider = null;
192
+ this._deferredProvider.completer = null;
149
193
  }
150
- // Build a new settings object containing the secrets.
151
- const fullSettings = {};
152
- for (const key of Object.keys(settings)) {
153
- if (settings[key] === SECRETS_REPLACEMENT) {
154
- const id = getSecretId(name, key);
155
- const secrets = await ((_b = this._secretsManager) === null || _b === void 0 ? void 0 : _b.get(Private.getToken(), SECRETS_NAMESPACE, id));
156
- if (secrets !== undefined) {
157
- fullSettings[key] = secrets.value;
158
- }
159
- continue;
194
+ const compatibilityCheck = this.getCompatibilityCheck(provider);
195
+ if (compatibilityCheck !== undefined) {
196
+ const error = await compatibilityCheck();
197
+ if (error !== null) {
198
+ this.completerError = error.trim();
199
+ Private.setName('completer', 'None');
200
+ this._providerChanged.emit('completer');
201
+ return;
160
202
  }
161
- fullSettings[key] = settings[key];
162
203
  }
163
- if (((_c = this._currentProvider) === null || _c === void 0 ? void 0 : _c.completer) !== undefined) {
204
+ // Get the settings including the secrets.
205
+ const fullSettings = await this._buildFullSettings(provider, settings);
206
+ if (currentProvider?.completer !== undefined) {
164
207
  try {
165
- this._completer = new this._currentProvider.completer({
208
+ Private.setCompleter(new currentProvider.completer({
209
+ providerRegistry: this,
166
210
  settings: fullSettings
167
- });
168
- this._completerError = '';
211
+ }));
169
212
  }
170
213
  catch (e) {
171
- this._completerError = e.message;
214
+ this.completerError = e.message;
172
215
  }
173
216
  }
174
217
  else {
175
- this._completer = null;
218
+ Private.setCompleter(null);
219
+ }
220
+ Private.setName('completer', provider);
221
+ this._providerChanged.emit('completer');
222
+ }
223
+ /**
224
+ * Set the chat provider.
225
+ * Creates the provider if the name has changed, otherwise only updates its config.
226
+ *
227
+ * @param options - An object with the name and the settings of the provider to use.
228
+ */
229
+ async setChatProvider(settings) {
230
+ this.chatError = '';
231
+ if (!Object.keys(settings).includes('provider')) {
232
+ Private.setName('chat', 'None');
233
+ Private.setChatModel(null);
234
+ this.chatError = 'The provider is missing from the chat settings';
235
+ return;
236
+ }
237
+ const provider = settings['provider'];
238
+ const currentProvider = Private.providers.get(provider) ?? null;
239
+ if (currentProvider === null) {
240
+ // The current provider may not be loaded when the settings are first loaded.
241
+ // Let's defer the provider loading.
242
+ this._deferredProvider.chat = settings;
243
+ Private.setName('chat', provider);
244
+ Private.setChatModel(null);
245
+ return;
246
+ }
247
+ else {
248
+ this._deferredProvider.chat = null;
176
249
  }
177
- if (((_d = this._currentProvider) === null || _d === void 0 ? void 0 : _d.chatModel) !== undefined) {
250
+ const compatibilityCheck = this.getCompatibilityCheck(provider);
251
+ if (compatibilityCheck !== undefined) {
252
+ const error = await compatibilityCheck();
253
+ if (error !== null) {
254
+ this.chatError = error.trim();
255
+ Private.setName('chat', 'None');
256
+ this._providerChanged.emit('chat');
257
+ return;
258
+ }
259
+ }
260
+ // Get the settings including the secrets.
261
+ const fullSettings = await this._buildFullSettings(provider, settings);
262
+ if (currentProvider?.chat !== undefined) {
178
263
  try {
179
- this._chatModel = new this._currentProvider.chatModel({
264
+ Private.setChatModel(new currentProvider.chat({
180
265
  ...fullSettings
181
- });
182
- this._chatError = '';
266
+ }));
183
267
  }
184
268
  catch (e) {
185
- this._chatError = e.message;
186
- this._chatModel = null;
269
+ this.chatError = e.message;
270
+ Private.setChatModel(null);
187
271
  }
188
272
  }
189
273
  else {
190
- this._chatModel = null;
274
+ Private.setChatModel(null);
191
275
  }
192
- this._name = name;
193
- this._providerChanged.emit();
276
+ Private.setName('chat', provider);
277
+ this._providerChanged.emit('chat');
194
278
  }
195
279
  /**
196
280
  * A signal emitting when the provider or its settings has changed.
@@ -198,6 +282,36 @@ export class AIProviderRegistry {
198
282
  get providerChanged() {
199
283
  return this._providerChanged;
200
284
  }
285
+ /**
286
+ * Build a new settings object containing the secrets.
287
+ */
288
+ async _buildFullSettings(name, settings) {
289
+ // Build a new settings object containing the secrets.
290
+ const fullSettings = {};
291
+ for (const key of Object.keys(settings)) {
292
+ if (settings[key] === SECRETS_REPLACEMENT) {
293
+ const id = getSecretId(name, key);
294
+ const secrets = await this._secretsManager?.get(Private.getToken(), SECRETS_NAMESPACE, id);
295
+ if (secrets !== undefined) {
296
+ fullSettings[key] = secrets.value;
297
+ }
298
+ continue;
299
+ }
300
+ fullSettings[key] = settings[key];
301
+ }
302
+ return fullSettings;
303
+ }
304
+ _secretsManager;
305
+ _providerChanged = new Signal(this);
306
+ _chatError = '';
307
+ _completerError = '';
308
+ _notifications;
309
+ _deferredProvider = {
310
+ chat: null,
311
+ completer: null
312
+ };
313
+ _chatPrompt = '';
314
+ _completerPrompt = '';
201
315
  }
202
316
  (function (AIProviderRegistry) {
203
317
  /**
@@ -239,21 +353,60 @@ export class AIProviderRegistry {
239
353
  var Private;
240
354
  (function (Private) {
241
355
  /**
242
- * The token to use with the secrets manager.
356
+ * The token to use with the secrets manager, setter and getter.
243
357
  */
244
358
  let secretsToken;
245
- /**
246
- * Set of the token.
247
- */
248
359
  function setToken(value) {
249
360
  secretsToken = value;
250
361
  }
251
362
  Private.setToken = setToken;
252
- /**
253
- * get the token.
254
- */
255
363
  function getToken() {
256
364
  return secretsToken;
257
365
  }
258
366
  Private.getToken = getToken;
367
+ /**
368
+ * The providers map, in private namespace to prevent updating the 'exposeChatModel'
369
+ * flag.
370
+ */
371
+ Private.providers = new Map();
372
+ /**
373
+ * The name of the current provider, setter and getter.
374
+ * It is in a private namespace to prevent updating it without updating the models.
375
+ */
376
+ const names = {
377
+ chat: 'None',
378
+ completer: 'None'
379
+ };
380
+ function setName(role, value) {
381
+ names[role] = value;
382
+ }
383
+ Private.setName = setName;
384
+ function getName(role) {
385
+ return names[role];
386
+ }
387
+ Private.getName = getName;
388
+ /**
389
+ * The chat model setter and getter.
390
+ */
391
+ let chatModel = null;
392
+ function setChatModel(model) {
393
+ chatModel = model;
394
+ }
395
+ Private.setChatModel = setChatModel;
396
+ function getChatModel() {
397
+ return chatModel;
398
+ }
399
+ Private.getChatModel = getChatModel;
400
+ /**
401
+ * The completer setter and getter.
402
+ */
403
+ let completer = null;
404
+ function setCompleter(model) {
405
+ completer = model;
406
+ }
407
+ Private.setCompleter = setCompleter;
408
+ function getCompleter() {
409
+ return completer;
410
+ }
411
+ Private.getCompleter = getCompleter;
259
412
  })(Private || (Private = {}));
@@ -1,3 +1,3 @@
1
1
  export * from './panel';
2
- export * from './settings-connector';
2
+ export * from './textarea';
3
3
  export * from './utils';
@@ -1,3 +1,3 @@
1
1
  export * from './panel';
2
- export * from './settings-connector';
2
+ export * from './textarea';
3
3
  export * from './utils';